Compare commits
500 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de4f90dd72 | ||
|
|
4a7b4754f0 | ||
|
|
fe05534a95 | ||
|
|
c7c7e75b32 | ||
|
|
efc6aed388 | ||
|
|
197521d5b1 | ||
|
|
23420f62df | ||
|
|
33149caede | ||
|
|
67bec7136b | ||
|
|
57a12114dc | ||
|
|
e32abea46b | ||
|
|
b12b271a61 | ||
|
|
f337cccc68 | ||
|
|
7f9e178f75 | ||
|
|
b19696090f | ||
|
|
d7488bd402 | ||
|
|
604299a1ac | ||
|
|
6800d51347 | ||
|
|
7cecd249a8 | ||
|
|
a214ea9341 | ||
|
|
884a19b13d | ||
|
|
771f4ae766 | ||
|
|
9273398c0e | ||
|
|
a5ed6ad134 | ||
|
|
1bbf575e91 | ||
|
|
49582fd841 | ||
|
|
bdaf12c1fa | ||
|
|
ef27c98056 | ||
|
|
722f3ce384 | ||
|
|
9d084a7b2f | ||
|
|
c31a06e255 | ||
|
|
ea36c79c26 | ||
|
|
cbe33caeef | ||
|
|
8b44354fec | ||
|
|
619302cd11 | ||
|
|
3e94ca11df | ||
|
|
f818778e0a | ||
|
|
280a91f139 | ||
|
|
82367a81c9 | ||
|
|
93b1234d0f | ||
|
|
571bc5cf90 | ||
|
|
91de353307 | ||
|
|
7ec394a8f2 | ||
|
|
451c6c07ca | ||
|
|
a6b0548426 | ||
|
|
f89463c4d8 | ||
|
|
3be63d85f2 | ||
|
|
d271b63aa4 | ||
|
|
fb46fd7101 | ||
|
|
64513bb9d1 | ||
|
|
ef56f82de9 | ||
|
|
f775379f42 | ||
|
|
1b377dd674 | ||
|
|
99837127a6 | ||
|
|
f2d205e576 | ||
|
|
bf5bde0e36 | ||
|
|
18314adce2 | ||
|
|
d1a7a0ee1f | ||
|
|
eb73025338 | ||
|
|
66cd3e08a0 | ||
|
|
aa8e525681 | ||
|
|
7a36f89124 | ||
|
|
3e56acab64 | ||
|
|
012670b349 | ||
|
|
44cb13644a | ||
|
|
bd8e6db092 | ||
|
|
96ae8ade5d | ||
|
|
04b1cee71e | ||
|
|
bd07cf859f | ||
|
|
e937906647 | ||
|
|
03618f38b5 | ||
|
|
94fdddb056 | ||
|
|
12dfc60f75 | ||
|
|
a383d12061 | ||
|
|
3131c6cb5d | ||
|
|
5f53297f58 | ||
|
|
cebd808674 | ||
|
|
30a07f037e | ||
|
|
4ef1387781 | ||
|
|
1578ce2ebd | ||
|
|
391fd6c960 | ||
|
|
ef7ac1d77b | ||
|
|
ca2610d74f | ||
|
|
8d8aa52b9b | ||
|
|
84ec0c3964 | ||
|
|
f55736599e | ||
|
|
b890235a82 | ||
|
|
2cc3bc5759 | ||
|
|
ca100ef7e9 | ||
|
|
721cad75a2 | ||
|
|
c3110a4ab7 | ||
|
|
452aabf89b | ||
|
|
adcd6734ef | ||
|
|
a68c1f1cf7 | ||
|
|
712eaf9f1e | ||
|
|
7e119fa2ac | ||
|
|
ac90ad0129 | ||
|
|
6b61e273a0 | ||
|
|
aab8e85f9d | ||
|
|
3959892c20 | ||
|
|
420ad6cd37 | ||
|
|
664bff544e | ||
|
|
6716de6635 | ||
|
|
4f50fbdfe4 | ||
|
|
009abb3fd5 | ||
|
|
191b4402e1 | ||
|
|
13bc347897 | ||
|
|
187e2f1330 | ||
|
|
8d2ec115f5 | ||
|
|
921596f6f8 | ||
|
|
a00987efc8 | ||
|
|
b1a35d9df8 | ||
|
|
08dfa4cab2 | ||
|
|
63ca695b51 | ||
|
|
55310247c2 | ||
|
|
456334af75 | ||
|
|
38ce047d9e | ||
|
|
14be7dead5 | ||
|
|
ab2e368c6f | ||
|
|
0e7ca594ed | ||
|
|
f742287496 | ||
|
|
cb37919e76 | ||
|
|
933fc26b66 | ||
|
|
8ea94175ac | ||
|
|
013fb12c00 | ||
|
|
1e6b5a1e4d | ||
|
|
aed20db328 | ||
|
|
332684f4e2 | ||
|
|
12d275c26b | ||
|
|
9b1312c7d9 | ||
|
|
874b069357 | ||
|
|
03a917c326 | ||
|
|
6a5560a0b1 | ||
|
|
6b0bbdc605 | ||
|
|
4c0608d47d | ||
|
|
2e1aede8b4 | ||
|
|
2c3e968710 | ||
|
|
ecf45803e0 | ||
|
|
2e4ede4251 | ||
|
|
4f52649f28 | ||
|
|
11e58ff88d | ||
|
|
a7c097a5a9 | ||
|
|
8c53686697 | ||
|
|
6754f1467a | ||
|
|
fb98b3cc9a | ||
|
|
729f6fd308 | ||
|
|
e2b0711271 | ||
|
|
eb8cffb1a8 | ||
|
|
d7e534ca74 | ||
|
|
347e261748 | ||
|
|
7deb9c4fbf | ||
|
|
c1ab5ad929 | ||
|
|
2686c37aa1 | ||
|
|
45edb9973d | ||
|
|
2bd4ce08c4 | ||
|
|
460d1ac86c | ||
|
|
9df8da0b6f | ||
|
|
baaf85f567 | ||
|
|
6ffe817e86 | ||
|
|
edbdbdac56 | ||
|
|
11c3c6d20e | ||
|
|
81019b9fc8 | ||
|
|
4514123279 | ||
|
|
0fc4a448aa | ||
|
|
bde51cc946 | ||
|
|
4f7e29163f | ||
|
|
7837d1f6e8 | ||
|
|
99bb300559 | ||
|
|
f1108ef7d1 | ||
|
|
bd6cfec71c | ||
|
|
ef3ed86096 | ||
|
|
097898b120 | ||
|
|
6fbf2643a1 | ||
|
|
a1989c105e | ||
|
|
97426e6d7d | ||
|
|
b2e1b65ae5 | ||
|
|
06c8d34451 | ||
|
|
ecba81ea5b | ||
|
|
26e0066c82 | ||
|
|
6ebb9b6f66 | ||
|
|
102e30c29a | ||
|
|
a8a716e0bd | ||
|
|
9e23aaa5c0 | ||
|
|
9b059c3985 | ||
|
|
2d3c7e65d2 | ||
|
|
9e7d3462ab | ||
|
|
aececf980b | ||
|
|
e5f3b4bf1d | ||
|
|
9b629bb1c4 | ||
|
|
9e39a57231 | ||
|
|
47e9608aa2 | ||
|
|
2f012caa3e | ||
|
|
e835fc3ac0 | ||
|
|
5240e9ce98 | ||
|
|
b8ab2c839f | ||
|
|
1ee81b90bf | ||
|
|
1354f2debb | ||
|
|
7373ea24d8 | ||
|
|
7fc03461ba | ||
|
|
a5e45e2d79 | ||
|
|
42cf2ac19b | ||
|
|
6098d064a7 | ||
|
|
cd79f576b7 | ||
|
|
4039a4a820 | ||
|
|
919085d829 | ||
|
|
88f27b39c4 | ||
|
|
b1f38a51fe | ||
|
|
ef0253ee9e | ||
|
|
3cf33af0e2 | ||
|
|
3eaa060aac | ||
|
|
cf4efee340 | ||
|
|
0b04a96e15 | ||
|
|
c1d928c503 | ||
|
|
479b21a722 | ||
|
|
ceeaeaf487 | ||
|
|
d5179b742b | ||
|
|
b38055c497 | ||
|
|
ff1e9e63d6 | ||
|
|
a1a4293851 | ||
|
|
f39cc8ee53 | ||
|
|
9fc5b90f25 | ||
|
|
e7c9c884e9 | ||
|
|
0469128917 | ||
|
|
656e38eae7 | ||
|
|
95721350da | ||
|
|
98d9e87356 | ||
|
|
a69cb4f4c2 | ||
|
|
f42ea74e26 | ||
|
|
d9b86f9922 | ||
|
|
605e9cfe6d | ||
|
|
dd53c7b200 | ||
|
|
f9b10dc9db | ||
|
|
daef491d3e | ||
|
|
95fde17d97 | ||
|
|
2c6cbb7799 | ||
|
|
3498d4317a | ||
|
|
611ef49d03 | ||
|
|
fd2212db7b | ||
|
|
4d8b8ba64c | ||
|
|
32fe70a354 | ||
|
|
e2652df546 | ||
|
|
d1427d5f99 | ||
|
|
ef814f3602 | ||
|
|
00acc8289e | ||
|
|
341f980974 | ||
|
|
1b7ea5bed3 | ||
|
|
ee1c92ffa0 | ||
|
|
0114a50f61 | ||
|
|
c7c9e105ef | ||
|
|
3e31e71116 | ||
|
|
e74b1d2210 | ||
|
|
1ca2f28187 | ||
|
|
9fc75b651e | ||
|
|
934a6acdd2 | ||
|
|
88adc5676f | ||
|
|
f27483ea7b | ||
|
|
6a11c620cf | ||
|
|
7281f15051 | ||
|
|
b1196fb09b | ||
|
|
27fb875c0d | ||
|
|
3cdeac5dfb | ||
|
|
e1c47ce5c3 | ||
|
|
d9523cb1bc | ||
|
|
bd2403388e | ||
|
|
7511208b8b | ||
|
|
4586d44c1f | ||
|
|
717e5b07d1 | ||
|
|
2c6082f454 | ||
|
|
586239292b | ||
|
|
7bd1c87bf6 | ||
|
|
6eded4cdc2 | ||
|
|
5e693f2274 | ||
|
|
b0e3aeed6c | ||
|
|
dd9ef38636 | ||
|
|
32df782470 | ||
|
|
1d9162930c | ||
|
|
152d02bcbe | ||
|
|
2cc4dc724b | ||
|
|
1ee14f4c69 | ||
|
|
7748019a76 | ||
|
|
8241a1d8a3 | ||
|
|
83e3373561 | ||
|
|
c8c126d444 | ||
|
|
c12b1d0670 | ||
|
|
5caf4f45a9 | ||
|
|
8f51ff2910 | ||
|
|
6b8436f825 | ||
|
|
c4b146b36b | ||
|
|
098b14884d | ||
|
|
47d8818028 | ||
|
|
e3bc50a163 | ||
|
|
34ba6a86c9 | ||
|
|
2369ef53ac | ||
|
|
8d3907ff65 | ||
|
|
2760eaca85 | ||
|
|
af0319cc66 | ||
|
|
e050539747 | ||
|
|
451756c764 | ||
|
|
afa40df7ad | ||
|
|
fb2b606d26 | ||
|
|
e0ebd47730 | ||
|
|
e97f4e8020 | ||
|
|
79f07b7350 | ||
|
|
c1c8829536 | ||
|
|
ef20371562 | ||
|
|
c08f275cf7 | ||
|
|
6f995fe350 | ||
|
|
68aec92d3a | ||
|
|
dda328210c | ||
|
|
c3f4f232e3 | ||
|
|
302f1da066 | ||
|
|
a6e65d33dd | ||
|
|
7b0cb29466 | ||
|
|
a3eedb14f4 | ||
|
|
74d73a1d17 | ||
|
|
92e7e8516c | ||
|
|
61ac0b7d20 | ||
|
|
8b4ea7fd7c | ||
|
|
90846cf770 | ||
|
|
a9e33ea282 | ||
|
|
eea006e787 | ||
|
|
9e58098e5c | ||
|
|
ff49f574d4 | ||
|
|
599d816a88 | ||
|
|
4b0dd04114 | ||
|
|
36a0fbf181 | ||
|
|
3cfd8f40da | ||
|
|
23173c4bf4 | ||
|
|
722c31ccd9 | ||
|
|
7623a01272 | ||
|
|
1b90fdce73 | ||
|
|
ce57db9526 | ||
|
|
8b9e47cd11 | ||
|
|
a87a261b80 | ||
|
|
989d561679 | ||
|
|
b349990b19 | ||
|
|
2104b49cee | ||
|
|
77606efb77 | ||
|
|
6164ad2ab1 | ||
|
|
a60d58dcb0 | ||
|
|
4e45d17ad2 | ||
|
|
c8e614d064 | ||
|
|
544beeb078 | ||
|
|
a00f0d8367 | ||
|
|
ea57290792 | ||
|
|
603a4afaf3 | ||
|
|
19e2772729 | ||
|
|
82ebb82a5f | ||
|
|
516ae6b68c | ||
|
|
59225a4b9c | ||
|
|
1da9d36ecc | ||
|
|
0787e5c738 | ||
|
|
8a661088bb | ||
|
|
91659a32af | ||
|
|
5f8cd7e5c4 | ||
|
|
bc3f62a766 | ||
|
|
92faedb4a5 | ||
|
|
6f7008dd91 | ||
|
|
447589541f | ||
|
|
45f5f50262 | ||
|
|
f77a27ecc3 | ||
|
|
09de2eb194 | ||
|
|
7747a5665b | ||
|
|
aa617b5106 | ||
|
|
8038e2e01a | ||
|
|
212b068a8d | ||
|
|
2bcea0b566 | ||
|
|
512d4f2fb9 | ||
|
|
b524546e9c | ||
|
|
f91e287207 | ||
|
|
0fcf0a6a88 | ||
|
|
f9f3d74a3b | ||
|
|
2bfd0f2cc0 | ||
|
|
cdeb2eebe5 | ||
|
|
5134516afc | ||
|
|
e14b5d99eb | ||
|
|
a0375f52cf | ||
|
|
69dbcf3947 | ||
|
|
79387730bc | ||
|
|
3e6b019044 | ||
|
|
1886b90be7 | ||
|
|
85421f9b4b | ||
|
|
3749ef8433 | ||
|
|
dfb3e98fec | ||
|
|
a40442c8a9 | ||
|
|
a7396d6bee | ||
|
|
507c4c67ff | ||
|
|
cbdc04e929 | ||
|
|
a6582a1da3 | ||
|
|
3d8f3df8f1 | ||
|
|
7b8dbdedb5 | ||
|
|
53b13eacaa | ||
|
|
fcc76d99ef | ||
|
|
9a2141025a | ||
|
|
bfe3285375 | ||
|
|
5cf9c6e073 | ||
|
|
5e00d77e69 | ||
|
|
a4dfb05d0c | ||
|
|
97a798e357 | ||
|
|
47aafe88f8 | ||
|
|
bc356ab8e5 | ||
|
|
dd5cdfd848 | ||
|
|
de2cb08a6b | ||
|
|
9a2550ad48 | ||
|
|
2f6e4c0736 | ||
|
|
a9c5cbf11f | ||
|
|
facd0d398a | ||
|
|
1b2fc5d403 | ||
|
|
9d290aa533 | ||
|
|
9046291128 | ||
|
|
59131b6b51 | ||
|
|
3725a23c49 | ||
|
|
812c42c89a | ||
|
|
285d7d3265 | ||
|
|
134789602b | ||
|
|
55f49b5f78 | ||
|
|
cd8067285f | ||
|
|
10c09e9375 | ||
|
|
bb70c0733a | ||
|
|
3dad2b31bb | ||
|
|
7844acd70f | ||
|
|
f01b0b3404 | ||
|
|
10fee6e144 | ||
|
|
c4c8a8b703 | ||
|
|
7053a1e42a | ||
|
|
fd3c4261de | ||
|
|
d21e475d12 | ||
|
|
48bdf0b2a9 | ||
|
|
ac1c652854 | ||
|
|
3b286de9db | ||
|
|
cbcb696cfd | ||
|
|
13320ea8cc | ||
|
|
bdf45f6a0b | ||
|
|
667b1e9f99 | ||
|
|
e52e82fb78 | ||
|
|
78cb3f8ee2 | ||
|
|
a0f476fb24 | ||
|
|
13c32bfe53 | ||
|
|
a54da059ef | ||
|
|
76fe0def4f | ||
|
|
9a25b747ae | ||
|
|
aeefd3e873 | ||
|
|
f9efdcb654 | ||
|
|
be672cbcba | ||
|
|
c4ca7e471a | ||
|
|
bbce082ec2 | ||
|
|
781214a2f0 | ||
|
|
7942eaf64b | ||
|
|
36cd3c0c42 | ||
|
|
620bccb102 | ||
|
|
3ca79bebc7 | ||
|
|
a1add0cfdb | ||
|
|
dc22ad1ca1 | ||
|
|
010df5a081 | ||
|
|
71e8b22849 | ||
|
|
17dcc7ec80 | ||
|
|
138c17d4a3 | ||
|
|
565eed99ef | ||
|
|
1f13c47a46 | ||
|
|
ec5d12c940 | ||
|
|
bac19b2486 | ||
|
|
5fbfa1453c | ||
|
|
dab2001dcc | ||
|
|
5bff9eb4d8 | ||
|
|
e2102bdefa | ||
|
|
2a3e06610f | ||
|
|
d186240cfc | ||
|
|
0f0d4f06bb | ||
|
|
249af325c9 | ||
|
|
a7532f2154 | ||
|
|
1c0a00e9b2 | ||
|
|
b1b8066fe5 | ||
|
|
25c2f3adad | ||
|
|
68e47d70fd | ||
|
|
af3b3f4ee5 | ||
|
|
1b63d514ac | ||
|
|
c6453a6e10 | ||
|
|
3f3f5fdaa0 | ||
|
|
2f72721ee2 | ||
|
|
92d99c6d64 | ||
|
|
f3b26b1101 | ||
|
|
4f0a5bcf15 | ||
|
|
ffb1a8ad84 | ||
|
|
70c07cf731 | ||
|
|
b2d946794b | ||
|
|
90617c5433 | ||
|
|
278dc34b44 | ||
|
|
dcd5d7b534 | ||
|
|
2d48465e1d | ||
|
|
6bda493f6e | ||
|
|
5459b8bb7a | ||
|
|
e410939bde | ||
|
|
047de2d747 | ||
|
|
997f4d35c2 | ||
|
|
ca547f1239 | ||
|
|
c9665773e2 | ||
|
|
eeeea6b735 | ||
|
|
bbd6a2b5d5 | ||
|
|
d33db2a0fe | ||
|
|
1f2588d677 |
1
.ruby-gemset
Normal file
1
.ruby-gemset
Normal file
@@ -0,0 +1 @@
|
||||
wpscan
|
||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@@ -0,0 +1 @@
|
||||
2.1.3
|
||||
10
.travis.yml
10
.travis.yml
@@ -4,4 +4,12 @@ rvm:
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
- 2.1.0
|
||||
script: bundle exec rspec --format documentation
|
||||
- 2.1.1
|
||||
- 2.1.2
|
||||
script: bundle exec rspec
|
||||
notifications:
|
||||
email:
|
||||
- wpscanteam@gmail.com
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rvm: 1.9.2
|
||||
|
||||
149
CHANGELOG.md
149
CHANGELOG.md
@@ -1,6 +1,145 @@
|
||||
# Changelog
|
||||
## Master
|
||||
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.3...master)
|
||||
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.5.1...master)
|
||||
|
||||
## Version 2.5.1
|
||||
Released: 2014-09-29
|
||||
|
||||
Fixes reference URL to WPVDB
|
||||
|
||||
## Version 2.5
|
||||
Released: 2014-09-26 (@ BruCON 2014)
|
||||
|
||||
New
|
||||
* Exit program after --update
|
||||
* Detect directory listing in upload folder
|
||||
* Be more verbose when no version can be detected
|
||||
* Added detection for Yoast Wordpress SEO plugin
|
||||
* Also ensure to not process empty Location headers
|
||||
* Ensures a nil location is not processed when enumerating usernames
|
||||
* Fix #626 - Detect 'Must_Use_Plugins'
|
||||
* better username extraction
|
||||
* Add a --cookie option. Ref #485
|
||||
* Add a --no-color option
|
||||
* Output: Give 'Fixed in' an informational tag
|
||||
* Added ArchAssault distro - WPScan comes pre-installed with this distro
|
||||
* Layout changes with new colors
|
||||
|
||||
Removed
|
||||
* Removes the source code updaters
|
||||
* Removes the ListGenerator plugin from WPStools
|
||||
* Removes all files from data/
|
||||
|
||||
General core
|
||||
* Update docs to reflect new updating logic
|
||||
* Little output change and coloring
|
||||
* Adds a missing verbose output
|
||||
* Re-build redirection url if begin with slash '/'
|
||||
* Fixes the remove_conditional_comments function
|
||||
* Ensures to give a string to Typhoeus
|
||||
* Fix wpstools check-vuln-ref-urls
|
||||
* Fix rspecs for new json
|
||||
* Only output if different from style_url
|
||||
* Add exception so 'ruby wpscan.rb http://domain.com' is detected
|
||||
* Added make to Debian installation, which is needed in minimal installation.
|
||||
* Add build-essentials requirement to Ubuntu > 14.04
|
||||
* Updated installation instr. for GNU/Linux Debian.
|
||||
* Changes VersionCompare#is_newer_or_same? by lesser_or_equal?
|
||||
* Fixes the location of the robots.txt check
|
||||
* Updates the recommended ruby version
|
||||
* Rspec 3.0 support
|
||||
* Adds ruby 2.1.2 to Travis
|
||||
* Updated ruby-progressbar to 1.5.0
|
||||
|
||||
WordPress Fingerprints
|
||||
* Adds WP 4.0 fingerprints
|
||||
* Adds WP 3.9.2, 3.8.4 & 3.7.4 fingerprints - Ref #652
|
||||
* Adds 3.9.1 fingerprints
|
||||
|
||||
Fixed issues
|
||||
* Fix #689 - Adds config file to check
|
||||
* Fix #694 - Output Arrays
|
||||
* Fix #693 - Adds pathname require statement
|
||||
* Fix #657 - generate method
|
||||
* Fix #685 - Potenial fix for 'marshal data too short' error
|
||||
* Fix #686 - Adds specs for relative URI in Location headers
|
||||
* Fix #435 - Update license
|
||||
* Fix #674 - Improves the Plugins & Themes passive detection
|
||||
* Fix #673 - Problem with the output
|
||||
* Fix #661 - Don't hash directories named like a file
|
||||
* Fix #653 - Fix for infinite loop in wpstools
|
||||
* Fix #625 - Only parse styles when needed
|
||||
* Fix #481 - Fix for Jetpack plugin false positive
|
||||
* Fix #480 - Properly removes the colour sequence from log
|
||||
* Fix #472 - WPScan stops after redirection if not WordPress website
|
||||
* Fix #464 - Readmes updated to reflect recent changes about the config file & batch mode
|
||||
|
||||
Vulnerabilities
|
||||
* geoplaces4 also uses name GeoPlaces4beta
|
||||
* Added metasploit module's
|
||||
* Added some timthumb detections
|
||||
|
||||
WPScan Database Statistics:
|
||||
* Total vulnerable versions: 87
|
||||
* Total vulnerable plugins: 854
|
||||
* Total vulnerable themes: 303
|
||||
* Total version vulnerabilities: 752
|
||||
* Total plugin vulnerabilities: 1351
|
||||
* Total theme vulnerabilities: 345
|
||||
|
||||
## Version 2.4
|
||||
Released: 2014-04-17
|
||||
|
||||
New
|
||||
* '--batch' switch option added - Fix #454
|
||||
* Add random-agent
|
||||
* Added more CLI options
|
||||
* Switch over to nist - Fix #301
|
||||
* New choice added when a redirection is detected - Fix #438
|
||||
|
||||
Removed
|
||||
* Removed 'Total WordPress Sites in the World' counter from stats
|
||||
* Old wpscan repo links removed - Fix #440
|
||||
* Fingerprinting Dev script removed
|
||||
* Useless code removed
|
||||
|
||||
General core
|
||||
* Rspecs update
|
||||
* Forcing Travis notify the team
|
||||
* Ruby 2.1.1 added to Travis
|
||||
* Equal output layout for interaction questions
|
||||
* Only output error trace if verbose if enabled
|
||||
* Memory improvements during wp-items enumerations
|
||||
* Fixed broken link checker, fixed some broken links
|
||||
* Couple more 404s fixed
|
||||
* Themes & Plugins list updated
|
||||
|
||||
WordPress Fingerprints
|
||||
* WP 3.8.2 & 3.7.2 Fingerprints added - Fix #448
|
||||
* WP 3.8.3 & 3.7.3 fingerprints
|
||||
* WP 3.9 fingerprints
|
||||
|
||||
Fixed issues
|
||||
* Fix #380 - Redirects in WP 3.6-3.0
|
||||
* Fix #413 - Check the version of the Timthumbs files found
|
||||
* Fix #429 - Error WpScan Cache Browser
|
||||
* Fix #431 - Version number comparison between '2.3.3' and '0.42b'
|
||||
* Fix #439 - Detect if the target goes down during the scan
|
||||
* Fix #451 - Do not rely only on files in wp-content for fingerprinting
|
||||
* Fix #453 - Documentation or inplemention of option parameters
|
||||
* Fix #455 - Fails with a message if the target returns a 403 during the wordpress check
|
||||
|
||||
Vulnerabilities
|
||||
* Update WordPress Vulnerabilities
|
||||
* Fixed some duplicate vulnerabilities
|
||||
|
||||
WPScan Database Statistics:
|
||||
* Total vulnerable versions: 79; 1 is new
|
||||
* Total vulnerable plugins: 748; 55 are new
|
||||
* Total vulnerable themes: 292; 41 are new
|
||||
* Total version vulnerabilities: 617; 326 are new
|
||||
* Total plugin vulnerabilities: 1162; 146 are new
|
||||
* Total theme vulnerabilities: 330; 47 are new
|
||||
|
||||
## Version 2.3
|
||||
Released: 2014-02-11
|
||||
@@ -12,7 +151,7 @@ New
|
||||
* New spell checker!
|
||||
* Added database modification dates in status report
|
||||
* Added 'Total WordPress Sites in the World' statistics
|
||||
* Added separator between Name and Version in Item
|
||||
* Added separator between Name and Version in Item
|
||||
* Added a "Work in progress" URL in the CHANGELOG
|
||||
|
||||
Removed
|
||||
@@ -44,7 +183,7 @@ WPScan Database Statistics:
|
||||
* Total plugin vulnerabilities: 1016; 236 are new
|
||||
* Total theme vulnerabilities: 283; 79 are new
|
||||
|
||||
Add WP Fingerprints
|
||||
WordPress Fingerprints
|
||||
* Better fingerprints
|
||||
* WP 3.8.1 Fingerprinting
|
||||
* WP 3.8 Fingerprinting
|
||||
@@ -53,10 +192,10 @@ Fixed issues
|
||||
* Fix #404 - Brute forcing issue over https
|
||||
* Fix #398 - Removed a fake vuln in WP Super Cache
|
||||
* Fix #393 - sudo added to the bundle install cmd for Mac OSX
|
||||
* Fix #228, #327 - Infinite loop when self-redirect
|
||||
* Fix #228, #327 - Infinite loop when self-redirect
|
||||
* Fix #201 - Incorrect Paramter Parsing when no url was supplied
|
||||
|
||||
## Version 2.2
|
||||
## Version 2.2
|
||||
Released: 2013-11-12
|
||||
|
||||
New
|
||||
|
||||
7
CREDITS
7
CREDITS
@@ -11,10 +11,11 @@ Ryan Dewhurst - @ethicalhack3r (Project Lead)
|
||||
|
||||
*Other Contributors*
|
||||
|
||||
Henri Salo AKA fgeek - Reported lots of vulnerabilities
|
||||
Alip AKA Undead - alip.aswalid at gmail.com
|
||||
michee08 - Reported and gave potential solutions to bugs.
|
||||
michee08 - Reported and gave potential solutions to bugs
|
||||
Callum Pember - Implemented proxy support - callumpember at gmail.com
|
||||
g0tmi1k - Additional timthumb checks + bug reports.
|
||||
g0tmi1k - Additional timthumb checks + bug reports
|
||||
Melvin Lammerts - Reported a couple of fake vulnerabilities - melvin at 12k.nl
|
||||
Paolo Perego - @thesp0nge - Basic authentication
|
||||
Gianluca Brindisi - @gbrindisi - Project Developer
|
||||
Gianluca Brindisi - @gbrindisi - Project Developer
|
||||
|
||||
2
DISCLAIMER.txt
Normal file
2
DISCLAIMER.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
WPScan is not responsible for misuse or for any damage that you may cause!
|
||||
You agree that you use this software at your own risk.
|
||||
19
Gemfile
19
Gemfile
@@ -1,13 +1,14 @@
|
||||
source "https://rubygems.org"
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "typhoeus", ">=0.6.3"
|
||||
gem "nokogiri"
|
||||
gem "json"
|
||||
gem "terminal-table"
|
||||
gem "ruby-progressbar", ">=1.2.0"
|
||||
gem 'typhoeus', '~>0.6.8'
|
||||
gem 'nokogiri'
|
||||
gem 'json'
|
||||
gem 'terminal-table'
|
||||
gem 'ruby-progressbar', '>=1.6.0'
|
||||
|
||||
group :test do
|
||||
gem "webmock", ">=1.17.2"
|
||||
gem "simplecov"
|
||||
gem "rspec", :require => "spec"
|
||||
gem 'webmock', '>=1.17.2'
|
||||
gem 'simplecov'
|
||||
gem 'rspec', '~>3.0'
|
||||
gem 'rspec-its'
|
||||
end
|
||||
|
||||
29
LICENSE
29
LICENSE
@@ -1,15 +1,20 @@
|
||||
WPScan - WordPress Security Scanner
|
||||
Copyright (C) 2012-2013
|
||||
The WPScan software and its data (henceforth both referred to simply as "WPScan") is dual-licensed - copyright 2011-2014 The WPScan Team.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, the system can be used under the terms of the GNU General Public License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Cases of commercialization are:
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
- Using WPScan to provide commercial managed/Software-as-a-Service services.
|
||||
- Distributing WPScan as a commercial product or as part of one.
|
||||
|
||||
Cases which do not require a commercial license, and thus fall under the terms of GNU General Public License, include (but are not limited to):
|
||||
|
||||
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit. So long as that does not conflict with the commercialization clause.
|
||||
- Using WPScan to test your own systems.
|
||||
- Any non-commercial use of WPScan.
|
||||
|
||||
If you need to acquire a commercial license or are unsure about whether you need to acquire a commercial license, please get in touch, we will be happy to clarify things for you and work with you to accommodate your requirements.
|
||||
|
||||
wpscanteam at gmail.com
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
126
README
126
README
@@ -9,23 +9,26 @@ __________________________________________________
|
||||
|
||||
==LICENSE==
|
||||
|
||||
WPScan - WordPress Security Scanner
|
||||
Copyright (C) 2011-2013 The WPScan Team
|
||||
The WPScan software and its data (henceforth both referred to simply as "WPScan") is dual-licensed - copyright 2011-2014 The WPScan Team.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, the system can be used under the terms of the GNU General Public License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Cases of commercialization are:
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
- Using WPScan to provide commercial managed/Software-as-a-Service services.
|
||||
- Distributing WPScan as a commercial product or as part of one.
|
||||
|
||||
ryandewhurst at gmail
|
||||
Cases which do not require a commercial license, and thus fall under the terms of GNU General Public License, include (but are not limited to):
|
||||
|
||||
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit. So long as that does not conflict with the commercialization clause.
|
||||
- Using WPScan to test your own systems.
|
||||
- Any non-commercial use of WPScan.
|
||||
|
||||
If you need to acquire a commercial license or are unsure about whether you need to acquire a commercial license, please get in touch, we will be happy to clarify things for you and work with you to accommodate your requirements.
|
||||
|
||||
wpscanteam at gmail.com
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
==INSTALL==
|
||||
|
||||
@@ -35,22 +38,38 @@ ryandewhurst at gmail
|
||||
* Kali Linux
|
||||
* Pentoo
|
||||
* SamuraiWTF
|
||||
* ArchAssault
|
||||
|
||||
Prerequisites:
|
||||
|
||||
* Windows not supported
|
||||
* Ruby >= 1.9.2 - Recommended: 1.9.3
|
||||
* Ruby >= 1.9.2 - Recommended: 2.1.2
|
||||
* Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
||||
* RubyGems - Recommended: latest
|
||||
* Git
|
||||
|
||||
-> Installing on Debian/Ubuntu:
|
||||
-> Installing on Ubuntu:
|
||||
|
||||
Before Ubuntu 14.04:
|
||||
|
||||
sudo apt-get install libcurl4-gnutls-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev
|
||||
|
||||
From Ubuntu 14.04:
|
||||
|
||||
sudo apt-get install libcurl4-gnutls-dev libxml2 libxml2-dev libxslt1-dev ruby-dev build-essential
|
||||
|
||||
sudo apt-get install libcurl4-gnutls-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler && bundle install --without test
|
||||
|
||||
-> Installing on Debian:
|
||||
|
||||
sudo apt-get install git ruby ruby-dev libcurl4-gnutls-dev make
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler
|
||||
bundle install --without test --path vendor/bundle
|
||||
|
||||
-> Installing on Fedora:
|
||||
|
||||
sudo yum install gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel libcurl-devel
|
||||
@@ -78,11 +97,24 @@ ryandewhurst at gmail
|
||||
cd wpscan
|
||||
sudo gem install bundler && sudo bundle install --without test
|
||||
|
||||
-> Installing with RVM:
|
||||
|
||||
cd ~
|
||||
curl -sSL https://get.rvm.io | bash -s stable
|
||||
source ~/.rvm/scripts/rvm
|
||||
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
|
||||
rvm install 2.1.2
|
||||
rvm use 2.1.2 --default
|
||||
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
|
||||
gem install bundler
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
bundle install --without test
|
||||
|
||||
==KNOWN ISSUES==
|
||||
|
||||
- Typhoeus segmentation fault:
|
||||
Update cURL to version => 7.21 (may have to install from source)
|
||||
See http://code.google.com/p/wpscan/issues/detail?id=81
|
||||
|
||||
- Proxy not working:
|
||||
Update cURL to version => 7.21.7 (may have to install from source).
|
||||
@@ -117,7 +149,7 @@ ryandewhurst at gmail
|
||||
|
||||
==WPSCAN ARGUMENTS==
|
||||
|
||||
--update Update to the latest revision
|
||||
--update Update the databases.
|
||||
|
||||
--url | -u <target url> The WordPress URL/domain to scan.
|
||||
|
||||
@@ -132,15 +164,19 @@ ryandewhurst at gmail
|
||||
ap all plugins (can take a long time)
|
||||
tt timthumbs
|
||||
t themes
|
||||
vp only vulnerable themes
|
||||
vt only vulnerable themes
|
||||
at all themes (can take a long time)
|
||||
Multiple values are allowed : '-e tt,p' will enumerate timthumbs and plugins
|
||||
If no option is supplied, the default is 'vt,tt,u,vp'
|
||||
Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins
|
||||
If no option is supplied, the default is "vt,tt,u,vp"
|
||||
|
||||
--exclude-content-based '<regexp or string>' Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied
|
||||
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)
|
||||
--exclude-content-based "<regexp or string>" Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied
|
||||
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)
|
||||
|
||||
--config-file | -c <config file> Use the specified config file
|
||||
--config-file | -c <config file> Use the specified config file, see the example.conf.json
|
||||
|
||||
--user-agent | -a <User-Agent> Use the specified User-Agent
|
||||
|
||||
--random-agent | -r Use a random User-Agent
|
||||
|
||||
--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not
|
||||
|
||||
@@ -148,23 +184,35 @@ ryandewhurst at gmail
|
||||
|
||||
--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed
|
||||
|
||||
--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).
|
||||
HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used
|
||||
--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).
|
||||
HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used
|
||||
|
||||
--proxy-auth <username:password> Supply the proxy login credentials (will override the one from conf/browser.conf.json).
|
||||
--proxy-auth <username:password> Supply the proxy login credentials.
|
||||
|
||||
--basic-auth <username:password> Set the HTTP Basic authentication
|
||||
--basic-auth <username:password> Set the HTTP Basic authentication.
|
||||
|
||||
--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute.
|
||||
|
||||
--threads | -t <number of threads> The number of threads to use when multi-threading requests. (will override the value from conf/browser.conf.json)
|
||||
--threads | -t <number of threads> The number of threads to use when multi-threading requests.
|
||||
|
||||
--username | -U <username> Only brute force the supplied username.
|
||||
|
||||
--cache-ttl <cache-ttl> Typhoeus cache TTL.
|
||||
|
||||
--request-timeout <request-timeout> Request Timeout.
|
||||
|
||||
--connect-timeout <connect-timeout> Connect Timeout.
|
||||
|
||||
--max-threads <max-threads> Maximum Threads.
|
||||
|
||||
--help | -h This help screen.
|
||||
|
||||
--verbose | -v Verbose output.
|
||||
|
||||
--batch Never ask for user input, use the default behaviour.
|
||||
|
||||
--no-color Do not use colors in the output.
|
||||
|
||||
==WPSCAN EXAMPLES==
|
||||
|
||||
Do 'non-intrusive' checks...
|
||||
@@ -191,7 +239,7 @@ Use custom content directory...
|
||||
|
||||
ruby wpscan.rb -u www.example.com --wp-content-dir custom-content
|
||||
|
||||
Update WPScan...
|
||||
Update WPScan's databases...
|
||||
|
||||
ruby wpscan.rb --update
|
||||
|
||||
@@ -201,21 +249,19 @@ Debug output...
|
||||
|
||||
==WPSTOOLS ARGUMENTS==
|
||||
|
||||
--help | -h This help screen.
|
||||
--Verbose | -v Verbose output.
|
||||
--update | -u Update to the latest revision.
|
||||
--generate_plugin_list [number of pages] Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)
|
||||
--gpl Alias for --generate_plugin_list
|
||||
--check-local-vulnerable-files | --clvf <local directory> Perform a recursive scan in the <local directory> to find vulnerable files or shells
|
||||
-v, --verbose Verbose output
|
||||
--check-vuln-ref-urls, --cvru Check all the vulnerabilities reference urls for 404
|
||||
--check-local-vulnerable-files, --clvf LOCAL_DIRECTORY Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells
|
||||
s, --stats Show WpScan Database statistics.
|
||||
--spellcheck, --sc Check all files for common spelling mistakes.
|
||||
|
||||
==WPSTOOLS EXAMPLES==
|
||||
|
||||
- Generate a new 'most popular' plugin list, up to 150 pages ...
|
||||
ruby wpstools.rb --generate_plugin_list 150
|
||||
|
||||
- Locally scan a wordpress installation for vulnerable files or shells :
|
||||
Locally scan a wordpress installation for vulnerable files or shells:
|
||||
ruby wpstools.rb --check-local-vulnerable-files /var/www/wordpress/
|
||||
|
||||
Or check https://github.com/fgeek/pyfiscan project.
|
||||
|
||||
===PROJECT HOME===
|
||||
|
||||
www.wpscan.org
|
||||
|
||||
194
README.md
194
README.md
@@ -1,26 +1,29 @@
|
||||

|
||||

|
||||
|
||||
[](https://travis-ci.org/wpscanteam/wpscan)
|
||||
|
||||
#### LICENSE
|
||||
|
||||
WPScan - WordPress Security Scanner
|
||||
Copyright (C), 2011-2013 The WPScan Team
|
||||
The WPScan software and its data (henceforth both referred to simply as "WPScan") is dual-licensed - copyright 2011-2014 The WPScan Team.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, the system can be used under the terms of the GNU General Public License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Cases of commercialization are:
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
- Using WPScan to provide commercial managed/Software-as-a-Service services.
|
||||
- Distributing WPScan as a commercial product or as part of one.
|
||||
|
||||
ryandewhurst at gmail
|
||||
Cases which do not require a commercial license, and thus fall under the terms of GNU General Public License, include (but are not limited to):
|
||||
|
||||
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit. So long as that does not conflict with the commercialization clause.
|
||||
- Using WPScan to test your own systems.
|
||||
- Any non-commercial use of WPScan.
|
||||
|
||||
If you need to acquire a commercial license or are unsure about whether you need to acquire a commercial license, please get in touch, we will be happy to clarify things for you and work with you to accommodate your requirements.
|
||||
|
||||
wpscanteam at gmail.com
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#### INSTALL
|
||||
|
||||
@@ -30,74 +33,89 @@ WPScan comes pre-installed on the following Linux distributions:
|
||||
- [Kali Linux](http://www.kali.org/)
|
||||
- [Pentoo](http://www.pentoo.ch/)
|
||||
- [SamuraiWTF](http://samurai.inguardians.com/)
|
||||
- [ArchAssault](https://archassault.org/)
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Windows not supported
|
||||
- Ruby >= 1.9.2 - Recommended: 1.9.3
|
||||
- Ruby >= 1.9.2 - Recommended: 2.1.2
|
||||
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
||||
- RubyGems - Recommended: latest
|
||||
- Git
|
||||
|
||||
*Installing on Debian/Ubuntu:*
|
||||
Windows is not supported.
|
||||
|
||||
```sudo apt-get install libcurl4-gnutls-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev```
|
||||
####Installing on Ubuntu:
|
||||
|
||||
```git clone https://github.com/wpscanteam/wpscan.git```
|
||||
Before Ubuntu 14.04:
|
||||
|
||||
```cd wpscan```
|
||||
sudo apt-get install libcurl4-gnutls-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev
|
||||
|
||||
```sudo gem install bundler && bundle install --without test```
|
||||
From Ubuntu 14.04:
|
||||
|
||||
*Installing on Fedora:*
|
||||
sudo apt-get install libcurl4-gnutls-dev libxml2 libxml2-dev libxslt1-dev ruby-dev build-essential
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler && bundle install --without test
|
||||
|
||||
```sudo yum install gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel libcurl-devel```
|
||||
####Installing on Debian:
|
||||
|
||||
```git clone https://github.com/wpscanteam/wpscan.git```
|
||||
sudo apt-get install git ruby ruby-dev libcurl4-gnutls-dev make
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler
|
||||
bundle install --without test --path vendor/bundle
|
||||
|
||||
```cd wpscan```
|
||||
####Installing on Fedora:
|
||||
|
||||
```sudo gem install bundler && bundle install --without test```
|
||||
sudo yum install gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel libcurl-devel
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler && bundle install --without test
|
||||
|
||||
*Installing on Archlinux:*
|
||||
####Installing on Archlinux:
|
||||
|
||||
```pacman -Syu ruby```
|
||||
pacman -Syu ruby
|
||||
pacman -Syu libyaml
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler && bundle install --without test
|
||||
gem install typhoeus
|
||||
gem install nokogiri
|
||||
|
||||
```pacman -Syu libyaml```
|
||||
####Installing on Mac OSX:
|
||||
|
||||
```git clone https://github.com/wpscanteam/wpscan.git```
|
||||
Apple Xcode, Command Line Tools and the libffi are needed (to be able to install the FFI gem), See [http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error](http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error)
|
||||
|
||||
```cd wpscan```
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
sudo gem install bundler && sudo bundle install --without test
|
||||
|
||||
```sudo gem install bundler && bundle install --without test```
|
||||
####Installing with RVM:
|
||||
|
||||
```gem install typhoeus```
|
||||
|
||||
```gem install nokogiri```
|
||||
|
||||
*Installing on Mac OSX:*
|
||||
|
||||
Apple Xcode, Command Line Tools and the libffi are needed (to be able to install the FFI gem), See http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error
|
||||
|
||||
```git clone https://github.com/wpscanteam/wpscan.git```
|
||||
|
||||
```cd wpscan```
|
||||
|
||||
```sudo gem install bundler && sudo bundle install --without test```
|
||||
cd ~
|
||||
curl -sSL https://get.rvm.io | bash -s stable
|
||||
source ~/.rvm/scripts/rvm
|
||||
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
|
||||
rvm install 2.1.2
|
||||
rvm use 2.1.2 --default
|
||||
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
|
||||
gem install bundler
|
||||
git clone https://github.com/wpscanteam/wpscan.git
|
||||
cd wpscan
|
||||
bundle install --without test
|
||||
|
||||
#### KNOWN ISSUES
|
||||
|
||||
- Typhoeus segmentation fault
|
||||
|
||||
Update cURL to version => 7.21 (may have to install from source)
|
||||
See http://code.google.com/p/wpscan/issues/detail?id=81
|
||||
|
||||
- Proxy not working
|
||||
|
||||
Update cURL to version => 7.21.7 (may have to install from source).
|
||||
|
||||
Installation from sources :
|
||||
```
|
||||
|
||||
Grab the sources from http://curl.haxx.se/download.html
|
||||
Decompress the archive
|
||||
Open the folder with the extracted files
|
||||
@@ -105,21 +123,21 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
||||
Run make
|
||||
Run sudo make install
|
||||
Run sudo ldconfig
|
||||
```
|
||||
|
||||
|
||||
- cannot load such file -- readline:
|
||||
|
||||
```sudo aptitude install libreadline5-dev libncurses5-dev```
|
||||
sudo aptitude install libreadline5-dev libncurses5-dev
|
||||
|
||||
Then, open the directory of the readline gem (you have to locate it)
|
||||
```
|
||||
|
||||
cd ~/.rvm/src/ruby-1.9.2-p180/ext/readline
|
||||
ruby extconf.rb
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
|
||||
See http://vvv.tobiassjosten.net/ruby-on-rails/fixing-readline-for-the-ruby-on-rails-console/ for more details
|
||||
See [http://vvv.tobiassjosten.net/ruby-on-rails/fixing-readline-for-the-ruby-on-rails-console/](http://vvv.tobiassjosten.net/ruby-on-rails/fixing-readline-for-the-ruby-on-rails-console/) for more details
|
||||
|
||||
- no such file to load -- rubygems
|
||||
|
||||
@@ -127,11 +145,11 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
||||
|
||||
And select your ruby version
|
||||
|
||||
See https://github.com/wpscanteam/wpscan/issues/148
|
||||
See [https://github.com/wpscanteam/wpscan/issues/148](https://github.com/wpscanteam/wpscan/issues/148)
|
||||
|
||||
#### WPSCAN ARGUMENTS
|
||||
|
||||
--update Update to the latest revision
|
||||
--update Update the databases.
|
||||
|
||||
--url | -u <target url> The WordPress URL/domain to scan.
|
||||
|
||||
@@ -148,13 +166,17 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
||||
t themes
|
||||
vt only vulnerable themes
|
||||
at all themes (can take a long time)
|
||||
Multiple values are allowed : '-e tt,p' will enumerate timthumbs and plugins
|
||||
If no option is supplied, the default is 'vt,tt,u,vp'
|
||||
Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins
|
||||
If no option is supplied, the default is "vt,tt,u,vp"
|
||||
|
||||
--exclude-content-based '<regexp or string>' Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied
|
||||
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)
|
||||
--exclude-content-based "<regexp or string>" Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied
|
||||
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)
|
||||
|
||||
--config-file | -c <config file> Use the specified config file
|
||||
--config-file | -c <config file> Use the specified config file, see the example.conf.json
|
||||
|
||||
--user-agent | -a <User-Agent> Use the specified User-Agent
|
||||
|
||||
--random-agent | -r Use a random User-Agent
|
||||
|
||||
--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not
|
||||
|
||||
@@ -162,23 +184,35 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
||||
|
||||
--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed
|
||||
|
||||
--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).
|
||||
HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used
|
||||
--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).
|
||||
HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used
|
||||
|
||||
--proxy-auth <username:password> Supply the proxy login credentials (will override the one from conf/browser.conf.json).
|
||||
--proxy-auth <username:password> Supply the proxy login credentials.
|
||||
|
||||
--basic-auth <username:password> Set the HTTP Basic authentication
|
||||
--basic-auth <username:password> Set the HTTP Basic authentication.
|
||||
|
||||
--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute.
|
||||
|
||||
--threads | -t <number of threads> The number of threads to use when multi-threading requests. (will override the value from conf/browser.conf.json)
|
||||
--threads | -t <number of threads> The number of threads to use when multi-threading requests.
|
||||
|
||||
--username | -U <username> Only brute force the supplied username.
|
||||
|
||||
--cache-ttl <cache-ttl> Typhoeus cache TTL.
|
||||
|
||||
--request-timeout <request-timeout> Request Timeout.
|
||||
|
||||
--connect-timeout <connect-timeout> Connect Timeout.
|
||||
|
||||
--max-threads <max-threads> Maximum Threads.
|
||||
|
||||
--help | -h This help screen.
|
||||
|
||||
--verbose | -v Verbose output.
|
||||
|
||||
--batch Never ask for user input, use the default behaviour.
|
||||
|
||||
--no-color Do not use colors in the output.
|
||||
|
||||
#### WPSCAN EXAMPLES
|
||||
|
||||
Do 'non-intrusive' checks...
|
||||
@@ -205,7 +239,7 @@ Use custom content directory...
|
||||
|
||||
```ruby wpscan.rb -u www.example.com --wp-content-dir custom-content```
|
||||
|
||||
Update WPScan...
|
||||
Update WPScan's databases...
|
||||
|
||||
```ruby wpscan.rb --update```
|
||||
|
||||
@@ -215,38 +249,40 @@ Debug output...
|
||||
|
||||
#### WPSTOOLS ARGUMENTS
|
||||
|
||||
--help | -h This help screen.
|
||||
--Verbose | -v Verbose output.
|
||||
--update | -u Update to the latest revision.
|
||||
--generate_plugin_list [number of pages] Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)
|
||||
--gpl Alias for --generate_plugin_list
|
||||
--check-local-vulnerable-files | --clvf <local directory> Perform a recursive scan in the <local directory> to find vulnerable files or shells
|
||||
-v, --verbose Verbose output
|
||||
--check-vuln-ref-urls, --cvru Check all the vulnerabilities reference urls for 404
|
||||
--check-local-vulnerable-files, --clvf LOCAL_DIRECTORY Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells
|
||||
-s, --stats Show WpScan Database statistics.
|
||||
--spellcheck, --sc Check all files for common spelling mistakes.
|
||||
|
||||
|
||||
#### WPSTOOLS EXAMPLES
|
||||
|
||||
Generate a new 'most popular' plugin list, up to 150 pages...
|
||||
Locally scan a wordpress installation for vulnerable files or shells:
|
||||
|
||||
```ruby wpstools.rb --generate_plugin_list 150```
|
||||
|
||||
Locally scan a wordpress installation for vulnerable files or shells :
|
||||
```ruby wpstools.rb --check-local-vulnerable-files /var/www/wordpress/```
|
||||
|
||||
Or check [pyfiscan](https://github.com/fgeek/pyfiscan) project.
|
||||
|
||||
#### PROJECT HOME
|
||||
|
||||
www.wpscan.org
|
||||
[http://www.wpscan.org](http://www.wpscan.org)
|
||||
|
||||
#### VULNERABILITY DATABASE
|
||||
|
||||
[https://www.wpvulndb.com](https://www.wpvulndb.com)
|
||||
|
||||
#### GIT REPOSITORY
|
||||
|
||||
https://github.com/wpscanteam/wpscan
|
||||
[https://github.com/wpscanteam/wpscan](https://github.com/wpscanteam/wpscan)
|
||||
|
||||
#### ISSUES
|
||||
|
||||
https://github.com/wpscanteam/wpscan/issues
|
||||
[https://github.com/wpscanteam/wpscan/issues](https://github.com/wpscanteam/wpscan/issues)
|
||||
|
||||
#### DEVELOPER DOCUMENTATION
|
||||
|
||||
http://rdoc.info/github/wpscanteam/wpscan/frames
|
||||
[http://rdoc.info/github/wpscanteam/wpscan/frames](http://rdoc.info/github/wpscanteam/wpscan/frames)
|
||||
|
||||
#### SPONSOR
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0",
|
||||
/* Modes :
|
||||
static : will use the defined user_agent for each request
|
||||
semi-static : will randomly choose a user agent into available_user_agents before each scan
|
||||
random : each request will choose a random user agent in available_user_agents
|
||||
*/
|
||||
"user_agent_mode": "static",
|
||||
|
||||
/* Uncomment the "proxy" line to use the proxy
|
||||
SOCKS proxies (4, 4A, 5) are supported, ie : "proxy": "socks5://127.0.0.1:9000"
|
||||
If you do not specify the protocol, http will be used
|
||||
*/
|
||||
//"proxy": "127.0.0.1:3128",
|
||||
//"proxy_auth": "username:password",
|
||||
|
||||
"cache_ttl": 600, // 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled
|
||||
|
||||
"request_timeout": 2000, // 2s
|
||||
|
||||
"connect_timeout": 1000, // 1s
|
||||
|
||||
"max_threads": 20,
|
||||
|
||||
// Some user_agents can be found there http://techpatterns.com/downloads/firefox/useragentswitcher.xml (thx to Gianluca Brindisi)
|
||||
"available_user_agents":
|
||||
[
|
||||
// Windows
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.27 (KHTML, like Gecko) Chrome/12.0.712.0 Safari/534.27",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0E)",
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0)",
|
||||
"Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5",
|
||||
|
||||
// MAC
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
||||
|
||||
// Linux
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100915 Gentoo Firefox/3.6.9",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.16) Gecko/20120421 Gecko Firefox/11.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0",
|
||||
"Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; us; rv:1.9.1.19) Gecko/20110430 shadowfox/7.0 (like Firefox/7.0"
|
||||
]
|
||||
}
|
||||
2
data/.gitignore
vendored
Normal file
2
data/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
Only he following extensions are scanned : js, php, swf, html, htm
|
||||
If you want to add one, modify the variable file_extension_to_scan, line 191 in wpstools.rb
|
||||
-->
|
||||
|
||||
<hashes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="local_vulnerable_files.xsd">
|
||||
|
||||
<hash sha1="17c372678aafb3bc1a7b37320b5cc1d8af433527">
|
||||
<title>XSS in swfupload.swf</title>
|
||||
<file>swfupload.swf</file>
|
||||
<reference>http://brindi.si/g/blog/vulnerable-swf-bundled-in-wordpress-plugins.html</reference>
|
||||
</hash>
|
||||
|
||||
<hash sha1="775dc1089829ef07838406def28a4d8bfef69d66">
|
||||
<title>Arbitrary File Upload Vulnerability</title>
|
||||
<file>php.php</file>
|
||||
<reference>http://packetstormsecurity.com/files/119241/wpvalums-shell.txt</reference>
|
||||
</hash>
|
||||
|
||||
<!-- This one a is the same as above, but the postSize verification has been removed -->
|
||||
<hash sha1="5e8f0d5a917d2937318a9bafd0529135bd473e70">
|
||||
<title>Arbitrary File Upload Vulnerability</title>
|
||||
<file>php.php</file>
|
||||
<reference>http://packetstormsecurity.com/files/119218/wpreflexgallery-shell.txt</reference>
|
||||
</hash>
|
||||
|
||||
<hash sha1="3f9ad05b05b65ee2b6efa1373f708293dd2005c7">
|
||||
<title>Arbitrary File Upload Vulnerability</title>
|
||||
<file>uploadify.php</file>
|
||||
<reference>http://packetstormsecurity.com/files/119219/wpuploader104-shell.txt</reference>
|
||||
</hash>
|
||||
|
||||
<hash sha1="ac638cc38f011b74a8d9a4e7d3d60358e472166c">
|
||||
<title>Inline phpinfo()</title>
|
||||
<file>phpinfo.php</file>
|
||||
<reference>http://php.net/manual/en/function.phpinfo.php</reference>
|
||||
</hash>
|
||||
|
||||
<hash sha1="012ee25cceff745e681fbb3697a06f3712f55554">
|
||||
<title>phpinfo()</title>
|
||||
<file>phpinfo.php</file>
|
||||
<reference>http://php.net/manual/en/function.phpinfo.php</reference>
|
||||
</hash>
|
||||
|
||||
</hashes>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:simpleType name="stringtype">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:whiteSpace value="preserve" />
|
||||
<xs:minLength value="1" />
|
||||
<xs:pattern value="[^\s].+[^\s]|[^\s]"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="uritype">
|
||||
<xs:restriction base="xs:anyURI">
|
||||
<xs:minLength value="1" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="sha1type">
|
||||
<xs:restriction base="stringtype">
|
||||
<xs:pattern value="[0-9a-f]{40}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="hashtype">
|
||||
<xs:sequence minOccurs="1" maxOccurs="1">
|
||||
<xs:element name="title" type="stringtype"/>
|
||||
<xs:element name="file" type="stringtype"/>
|
||||
<xs:element name="reference" type="uritype"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="sha1type" name="sha1" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="hashes">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="hash" type="hashtype" maxOccurs="unbounded" minOccurs="1"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
||||
@@ -1,3 +0,0 @@
|
||||
http://.*\.rr\.nu
|
||||
http://www\.thesea\.org/media\.php
|
||||
|
||||
10816
data/plugin_vulns.xml
10816
data/plugin_vulns.xml
File diff suppressed because it is too large
Load Diff
2199
data/plugins.txt
2199
data/plugins.txt
File diff suppressed because it is too large
Load Diff
40955
data/plugins_full.txt
40955
data/plugins_full.txt
File diff suppressed because it is too large
Load Diff
2990
data/theme_vulns.xml
2990
data/theme_vulns.xml
File diff suppressed because it is too large
Load Diff
300
data/themes.txt
300
data/themes.txt
@@ -1,300 +0,0 @@
|
||||
academica
|
||||
activetab
|
||||
adamos
|
||||
adelle
|
||||
admired
|
||||
adventure
|
||||
aldehyde
|
||||
alexandria
|
||||
analytical-lite
|
||||
anarcho-notepad
|
||||
andrina-lite
|
||||
appointment
|
||||
aquarius
|
||||
ascetica
|
||||
aspen
|
||||
asteria-lite
|
||||
asteroid
|
||||
atahualpa
|
||||
attitude
|
||||
autofocus
|
||||
beach
|
||||
bearded
|
||||
bicubic
|
||||
birdsite
|
||||
bizantine
|
||||
bizark
|
||||
bizflare
|
||||
bizkit
|
||||
biznez-lite
|
||||
bizsphere
|
||||
bizstudio-lite
|
||||
bizway
|
||||
blackbird
|
||||
blain
|
||||
blankslate
|
||||
blogbox
|
||||
blogly-lite
|
||||
blogolife
|
||||
blogotron
|
||||
blox
|
||||
blue-planet
|
||||
boldr-lite
|
||||
boot-store
|
||||
bootstrap-ultimate
|
||||
bota
|
||||
bouquet
|
||||
bresponzive
|
||||
brightnews
|
||||
bueno
|
||||
bushwick
|
||||
business-lite
|
||||
busiprof
|
||||
butterbelly
|
||||
buzz
|
||||
byblos
|
||||
carton
|
||||
catch-box
|
||||
catch-everest
|
||||
catch-evolution
|
||||
celestial-lite
|
||||
chaostheory
|
||||
childishly-simple
|
||||
chooko-lite
|
||||
church
|
||||
cirrus
|
||||
clean-retina
|
||||
coller
|
||||
colorway
|
||||
contango
|
||||
coraline
|
||||
corpo
|
||||
crates
|
||||
current
|
||||
custom-community
|
||||
customizr
|
||||
cyberchimps
|
||||
d5-socialia
|
||||
decode
|
||||
designfolio
|
||||
destro
|
||||
discover
|
||||
dms
|
||||
duena
|
||||
dusk-to-dawn
|
||||
duster
|
||||
dw-minion
|
||||
dw-wallpress
|
||||
dzonia-lite
|
||||
eclipse
|
||||
elisium
|
||||
engrave-lite
|
||||
enough
|
||||
envision
|
||||
epic
|
||||
esell
|
||||
esplanade
|
||||
esquire
|
||||
estate
|
||||
evolve
|
||||
expound
|
||||
family
|
||||
fashionistas
|
||||
fastr
|
||||
figero
|
||||
fine
|
||||
firmasite
|
||||
fixy
|
||||
flounder
|
||||
focus
|
||||
forestly
|
||||
forever
|
||||
formidable-restaurant
|
||||
frau
|
||||
fresh-lite
|
||||
frisco-for-buddypress
|
||||
frontier
|
||||
fruitful
|
||||
future
|
||||
gamepress
|
||||
gold
|
||||
golden-eagle-lite
|
||||
graphene
|
||||
gridbulletin
|
||||
gridiculous
|
||||
gridster-lite
|
||||
hatch
|
||||
hazen
|
||||
hero
|
||||
highwind
|
||||
hueman
|
||||
hypnotist
|
||||
iconic-one
|
||||
ifeature
|
||||
inkness
|
||||
inkzine
|
||||
intuition
|
||||
invert-lite
|
||||
iribbon
|
||||
isis
|
||||
journalism
|
||||
klasik
|
||||
leatherdiary
|
||||
leniy-radius
|
||||
limelight
|
||||
lizardbusiness
|
||||
local-business
|
||||
lugada
|
||||
magazine-basic
|
||||
magazine-style
|
||||
magazino
|
||||
mantra
|
||||
max-magazine
|
||||
meadowhill
|
||||
medicine
|
||||
mesocolumn
|
||||
mh-magazine-lite
|
||||
ming
|
||||
minimatica
|
||||
minimize
|
||||
modern-estate
|
||||
montezuma
|
||||
multiloquent
|
||||
neuro
|
||||
neutro
|
||||
newdark
|
||||
newlife
|
||||
newp
|
||||
newtek
|
||||
next-saturday
|
||||
nictitate
|
||||
omega
|
||||
one-page
|
||||
onecolumn
|
||||
openstrap
|
||||
opulus-sombre
|
||||
origami
|
||||
origin
|
||||
oxygen
|
||||
p2
|
||||
pagelines
|
||||
parabola
|
||||
parallax
|
||||
parament
|
||||
phonix
|
||||
photolistic
|
||||
piedmont
|
||||
pilcrow
|
||||
pilot-fish
|
||||
pinbin
|
||||
pinboard
|
||||
pink-touch-2
|
||||
pitch
|
||||
platform
|
||||
point
|
||||
portfolio-press
|
||||
pr-pin
|
||||
preference-lite
|
||||
preus
|
||||
primo-lite
|
||||
privatebusiness
|
||||
quark
|
||||
raindrops
|
||||
raptor
|
||||
raven
|
||||
ready-review
|
||||
reddle
|
||||
redify
|
||||
reizend
|
||||
response
|
||||
responsive
|
||||
restaurateur
|
||||
reviewgine-affiliate
|
||||
ridizain
|
||||
rtpanel
|
||||
rundown
|
||||
sampression-lite
|
||||
sensitive
|
||||
serene
|
||||
shopping
|
||||
sigma
|
||||
silverclean-lite
|
||||
simple-catch
|
||||
simpleo
|
||||
simplicity-lite
|
||||
sixteen
|
||||
sliding-door
|
||||
snaps
|
||||
snapshot
|
||||
sorbet
|
||||
spartan
|
||||
spasalon
|
||||
sporty
|
||||
spun
|
||||
stargazer
|
||||
startupwp
|
||||
steira
|
||||
strapvert
|
||||
suevafree
|
||||
suffusion
|
||||
sugar-and-spice
|
||||
suits
|
||||
sukelius-magazine
|
||||
sundance
|
||||
sunny-blue-sky
|
||||
sunspot
|
||||
supernova
|
||||
surfarama
|
||||
swift-basic
|
||||
syntax
|
||||
tanzanite
|
||||
teal
|
||||
techism
|
||||
tempera
|
||||
terrifico
|
||||
the-falcon
|
||||
thematic
|
||||
themia-lite
|
||||
theron-lite
|
||||
tiny-forge
|
||||
tonic
|
||||
travel-blogger
|
||||
travel-lite
|
||||
travelify
|
||||
twentyeleven
|
||||
twentyfourteen
|
||||
twentyten
|
||||
twentythirteen
|
||||
twentytwelve
|
||||
unite
|
||||
vantage
|
||||
venom
|
||||
viper
|
||||
virtue
|
||||
voyage
|
||||
ward
|
||||
weaver-ii
|
||||
weavr
|
||||
wiziapp-smooth-touch
|
||||
wordplus
|
||||
wp-advocate
|
||||
wp-barrister
|
||||
wp-creativix
|
||||
wp-opulus
|
||||
wp-simple
|
||||
writr
|
||||
x2
|
||||
yoko
|
||||
zalive
|
||||
zbench
|
||||
zeebizzcard
|
||||
zeebusiness
|
||||
zeedynamic
|
||||
zeeflow
|
||||
zeefocus
|
||||
zeeminty
|
||||
zeenoble
|
||||
zeestyle
|
||||
zeesynergie
|
||||
zeetasty
|
||||
zenon-lite
|
||||
7930
data/themes_full.txt
7930
data/themes_full.txt
File diff suppressed because it is too large
Load Diff
2459
data/timthumbs.txt
2459
data/timthumbs.txt
File diff suppressed because it is too large
Load Diff
108
data/vuln.xsd
108
data/vuln.xsd
@@ -1,108 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:simpleType name="stringtype">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:whiteSpace value="preserve" />
|
||||
<xs:minLength value="1" />
|
||||
<xs:pattern value="[^\s].+[^\s]|[^\s]"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="inttype">
|
||||
<xs:restriction base="xs:positiveInteger" />
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="uritype">
|
||||
<xs:restriction base="xs:anyURI">
|
||||
<xs:minLength value="1" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="cvetype">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="[0-9]{4}-[0-9]{4,}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="typetype">
|
||||
<xs:restriction base="stringtype">
|
||||
<xs:enumeration value="SQLI"/>
|
||||
<xs:enumeration value="MULTI"/>
|
||||
<xs:enumeration value="REDIRECT"/>
|
||||
<xs:enumeration value="RCE"/>
|
||||
<xs:enumeration value="RFI"/>
|
||||
<xs:enumeration value="LFI"/>
|
||||
<xs:enumeration value="UPLOAD"/>
|
||||
<xs:enumeration value="UNKNOWN"/>
|
||||
<xs:enumeration value="XSS"/>
|
||||
<xs:enumeration value="CSRF"/>
|
||||
<xs:enumeration value="SSRF"/>
|
||||
<xs:enumeration value="AUTHBYPASS"/>
|
||||
<xs:enumeration value="FPD"/>
|
||||
<xs:enumeration value="XXE"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="itemtype">
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="vulnerability" type="vulntype" />
|
||||
</xs:sequence>
|
||||
<xs:attribute type="stringtype" name="name" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="wordpresstype">
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="vulnerability" type="vulntype"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="stringtype" name="version" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="vulntype">
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:choice>
|
||||
<xs:element name="title" type="stringtype"/>
|
||||
<xs:element name="type" type="typetype"/>
|
||||
<xs:element name="fixed_in" type="stringtype"/>
|
||||
<xs:element name="references" type="referencetype"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="referencetype">
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:choice>
|
||||
<xs:element name="url" type="uritype" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="cve" type="cvetype" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="secunia" type="inttype" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="osvdb" type="inttype" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="metasploit" type="stringtype" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="exploitdb" type="inttype" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="vulnerabilities">
|
||||
<xs:complexType>
|
||||
<xs:choice>
|
||||
<xs:element name="plugin" type="itemtype" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element name="theme" type="itemtype" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element name="wordpress" type="wordpresstype" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:unique name="uniquePlugin">
|
||||
<xs:selector xpath="plugin"/>
|
||||
<xs:field xpath="@name"/>
|
||||
</xs:unique>
|
||||
<xs:unique name="uniqueTheme">
|
||||
<xs:selector xpath="theme"/>
|
||||
<xs:field xpath="@name"/>
|
||||
</xs:unique>
|
||||
<xs:unique name="uniqueWordpress">
|
||||
<xs:selector xpath="wordpress"/>
|
||||
<xs:field xpath="@version"/>
|
||||
</xs:unique>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
||||
@@ -1,173 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
This file contains identification data to identify WordPress versions.
|
||||
http://wordpress.org/download/release-archive/
|
||||
|
||||
Position is important, DO NOT change anything unless you know what you are doing :p
|
||||
-->
|
||||
|
||||
<wp-versions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="wp_versions.xsd">
|
||||
|
||||
<file src="wp-includes/css/buttons-rtl.css">
|
||||
<hash md5="fb062ed92b76638c161e80f4a5426586">
|
||||
<version>3.8.1</version>
|
||||
</hash>
|
||||
<hash md5="71c13ab1693b45fb3d7712e540c4dfe0">
|
||||
<version>3.8</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/tinymce/wp-tinymce.js.gz">
|
||||
<hash md5="44d281b0d84cc494e2b095a6d2202f4d">
|
||||
<version>3.7.1</version>
|
||||
</hash>
|
||||
<hash md5="b0bcf8091516db358ee9c833afd73175">
|
||||
<version>3.7</version>
|
||||
</hash>
|
||||
<hash md5="cf4bbd562430a9bcbe735062be851be1">
|
||||
<version>3.6.1</version>
|
||||
</hash>
|
||||
<hash md5="42ce18e88f1c21d4e991fcd431bcb606">
|
||||
<version>3.6</version>
|
||||
</hash>
|
||||
<hash md5="a58dd12608659503cf087e879e720354">
|
||||
<version>3.5.2</version>
|
||||
</hash>
|
||||
<hash md5="55c80a4794624ce9b94aa3631ad46c0b">
|
||||
<version>3.5.1</version>
|
||||
</hash>
|
||||
<hash md5="8e529a971610d7ebe7851339c5cb3d67">
|
||||
<version>3.5</version>
|
||||
</hash>
|
||||
<hash md5="ff19e44be975f89b647274d85b70f821">
|
||||
<version>3.4.2</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-admin/js/customize-controls.js">
|
||||
<hash md5="aa0d38bd6f590ad8c3126074145b1bf1">
|
||||
<version>3.4.1</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/customize-preview.js">
|
||||
<hash md5="da36bc2dfcb13350c799b62de68dfa4b">
|
||||
<version>3.4</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/plupload/plupload.js">
|
||||
<hash md5="85199c05db63fcb5880de4af8be7b571">
|
||||
<version>3.3.2</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="$wp-content$/themes/twentyeleven/style.css">
|
||||
<!-- same md5 for 3.3.2 -->
|
||||
<hash md5="030d3bac906ba69e9fbc99c5bac54a8e">
|
||||
<version>3.3.1</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-admin/js/common.js">
|
||||
<hash md5="4516252d47a73630280869994d510180">
|
||||
<version>3.3</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-admin/js/wp-fullscreen.js">
|
||||
<hash md5="5675f7793f171b6424bf72f9d7bf4d9a">
|
||||
<version>3.2.1</version>
|
||||
</hash>
|
||||
<hash md5="7b423e0b7c9221092737ad5271d09863">
|
||||
<version>3.2</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/css/admin-bar.css">
|
||||
<hash md5="181250fab3a7e2549a7e7fa21c2e6079">
|
||||
<version>3.1</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="$wp-content$/themes/twentyten/style.css">
|
||||
<hash md5="6211e2ac1463bf99e98f28ab63e47c54">
|
||||
<version>3.0</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="$wp-plugins$/akismet/readme.txt">
|
||||
<hash md5="4d5e52da417aa0101054bd41e6243389">
|
||||
<version>2.8.6</version>
|
||||
</hash>
|
||||
<hash md5="58e086dea9d24ed074fe84ba87386c69">
|
||||
<version>2.8.5</version>
|
||||
</hash>
|
||||
<hash md5="48c52025b5f28731e9a0c864c189c2e7">
|
||||
<version>2.8.2</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/wp-ajax-response.js">
|
||||
<hash md5="0289d1c13821599764774d55516ab81a">
|
||||
<version>2.7.1</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/thickbox/thickbox.css">
|
||||
<hash md5="9c2bd2be0893adbe02a0f864526734c2">
|
||||
<version>2.7</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/tinymce/plugins/wpeditimage/editor_plugin.js">
|
||||
<hash md5="5b140ddf0f08034402ae78b31d8a1a28">
|
||||
<version>2.6</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/tinymce/themes/advanced/js/image.js">
|
||||
<hash md5="088245408531c58bb52cc092294cc384">
|
||||
<version>2.5.1</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/tinymce/themes/advanced/js/link.js">
|
||||
<hash md5="19c6f3118728c38eb7779aab4847d2d9">
|
||||
<version>2.5</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-includes/js/wp-ajax.js">
|
||||
<hash md5="c5dbce0c3232c477033e0ce486c62755">
|
||||
<version>2.2</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="$wp-content$/themes/default/style.css">
|
||||
<hash md5="e44545f529a54de88209ce588676231c">
|
||||
<version>2.0.1</version>
|
||||
</hash>
|
||||
<hash md5="f786f66d3a40846aa22dcdfeb44fa562">
|
||||
<version>2.0</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="wp-layout.css">
|
||||
<hash md5="7140e06c00ed03d2bb3dad7672557510">
|
||||
<version>1.2.1</version>
|
||||
</hash>
|
||||
<hash md5="1bcc9253506c067eb130c9fc4f211a2f">
|
||||
<version>1.2-delta</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
<file src="layout2b.css">
|
||||
<hash md5="baec6b6ccbf71d8dced9f1bf67c751e1">
|
||||
<version>0.71-gold</version>
|
||||
</hash>
|
||||
</file>
|
||||
|
||||
</wp-versions>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:simpleType name="stringtype">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:whiteSpace value="preserve" />
|
||||
<xs:minLength value="1" />
|
||||
<xs:pattern value="[^\s].+[^\s]|[^\s]"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="filetype">
|
||||
<xs:sequence>
|
||||
<xs:element name="hash" type="hashtype" maxOccurs="unbounded" minOccurs="1"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="stringtype" name="src" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="md5type">
|
||||
<xs:restriction base="stringtype">
|
||||
<xs:pattern value="[0-9a-f]{32}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="hashtype">
|
||||
<xs:sequence minOccurs="1" maxOccurs="1">
|
||||
<xs:element name="version" type="stringtype"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="md5type" name="md5" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="wp-versions">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="file" type="filetype" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
</xs:schema>
|
||||
2384
data/wp_vulns.xml
2384
data/wp_vulns.xml
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# ln -sf /Users/xxx/wpscan/dev/pre-commit-hook.rb /Users/xxx/wpscan/.git/hooks/pre-commit
|
||||
# from the top level dir:
|
||||
# ln -sf ../../dev/pre-commit-hook.rb .git/hooks/pre-commit
|
||||
|
||||
require 'pty'
|
||||
html_path = 'rspec_results.html'
|
||||
|
||||
Binary file not shown.
@@ -1,237 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'rubygems'
|
||||
require 'uri'
|
||||
require 'dm-core'
|
||||
require 'dm-migrations'
|
||||
require 'dm-constraints'
|
||||
require 'optparse'
|
||||
require 'nokogiri'
|
||||
require 'typhoeus'
|
||||
|
||||
@db = "#{Dir.pwd}/wp-versions.db"
|
||||
|
||||
# return [ Array<String> ] The Stable versions (sorted by number DESC)
|
||||
def get_remote_wp_versions
|
||||
versions = []
|
||||
page = Nokogiri::HTML(Typhoeus.get('http://wordpress.org/download/release-archive/').body)
|
||||
|
||||
page.css('.widefat').first.css('tbody tr td:first').each do |node|
|
||||
versions << node.text.strip
|
||||
end
|
||||
versions.reverse
|
||||
end
|
||||
|
||||
def remove_dir(dir)
|
||||
%x{rm -rf #{dir}}
|
||||
end
|
||||
|
||||
def download(file_url, dest)
|
||||
%x{wget -q -np -O #{dest} #{file_url} > /dev/null}
|
||||
end
|
||||
|
||||
def wp_version_zip_url(version)
|
||||
"http://wordpress.org/wordpress-#{version}.zip"
|
||||
end
|
||||
|
||||
def wp_version_zip_md5(version)
|
||||
Typhoeus.get("#{wp_version_zip_url(version)}.md5").body
|
||||
end
|
||||
|
||||
def file_md5(file_path)
|
||||
Digest::MD5.file(file_path).hexdigest
|
||||
end
|
||||
|
||||
def web_page_md5(url)
|
||||
Digest::MD5.hexdigest(Typhoeus.get(url).body)
|
||||
end
|
||||
|
||||
def download_and_unzip_version(version, dest)
|
||||
dest_zip = "/tmp/wp-#{version}.zip"
|
||||
|
||||
download(wp_version_zip_url(version), dest_zip)
|
||||
|
||||
if $?.exitstatus === 0 and File.exists?(dest_zip)
|
||||
if file_md5(dest_zip) === wp_version_zip_md5(version)
|
||||
remove_dir("#{dest}/wordpress/")
|
||||
unzip(dest_zip, dest)
|
||||
|
||||
return true
|
||||
else
|
||||
raise 'Invalid md5'
|
||||
# Redownload the file ?
|
||||
end
|
||||
else
|
||||
raise 'Download error'
|
||||
end
|
||||
end
|
||||
|
||||
def unzip(zip_path, dest)
|
||||
%x{unzip -o -d #{dest} #{zip_path}}
|
||||
end
|
||||
|
||||
parser = OptionParser.new("Usage: ruby #{$0} [options]", 50) do |opts|
|
||||
opts.on('--db PATH-TO-DB', '-d', 'Path to the db, default: wp-versions.db') do |db|
|
||||
@db = db
|
||||
end
|
||||
|
||||
opts.on('--update', '-u', 'Update the db') do
|
||||
@update = true
|
||||
end
|
||||
|
||||
opts.on('--verbose', '-v', 'Verbose Mode') do
|
||||
@verbose = true
|
||||
end
|
||||
|
||||
opts.on('--show-unique-fingerprints WP-VERSION', '--suf', 'Output the unique file hashes for the given version of WordPress') do |version|
|
||||
@version = version
|
||||
end
|
||||
|
||||
opts.on('--search-hash HASH', '--sh', 'Search the hash and output the WP versions & file') do |hash|
|
||||
@hash = hash
|
||||
end
|
||||
|
||||
opts.on('--search-file RELATIVE-FILE-PATH', '--sf', 'Search the file and output the Wp versions & hashes') do |file|
|
||||
@file = file
|
||||
end
|
||||
|
||||
opts.on('--fingerprint URL', 'Fingerprint a remote wordpress blog') do |url|
|
||||
@target_url = url
|
||||
@target_url += '/' if @target_url[-1,1] != '/'
|
||||
end
|
||||
end
|
||||
parser.parse!
|
||||
|
||||
DataMapper::Logger.new($stdout, @verbose ? :debug : :fatal)
|
||||
DataMapper::setup(:default, "sqlite://#{@db}")
|
||||
|
||||
class Version
|
||||
include DataMapper::Resource
|
||||
|
||||
has n, :fingerprints, constraint: :destroy
|
||||
|
||||
property :id, Serial
|
||||
property :number, String, required: true, unique: true
|
||||
end
|
||||
|
||||
class Path
|
||||
include DataMapper::Resource
|
||||
|
||||
has n, :fingerprints, constraint: :destroy
|
||||
|
||||
property :id, Serial
|
||||
property :value, String, required: true, unique: true
|
||||
end
|
||||
|
||||
class Fingerprint
|
||||
include DataMapper::Resource
|
||||
|
||||
belongs_to :version, key: true
|
||||
belongs_to :path, key: true
|
||||
|
||||
property :md5_hash, String, required: true, length: 32
|
||||
|
||||
# DataMapper does not seem to support ordering by a column in a joining model
|
||||
# Solution found on StackOverflow ("DataMapper: Sorting results though association")
|
||||
def self.order_by_version(direction = :asc)
|
||||
order = DataMapper::Query::Direction.new(version.number, direction)
|
||||
query = all.query
|
||||
query.instance_variable_set('@order', [order])
|
||||
query.instance_variable_set('@links', [relationships['version'].inverse])
|
||||
all(query)
|
||||
end
|
||||
end
|
||||
|
||||
DataMapper.auto_upgrade!
|
||||
|
||||
# Update
|
||||
if @update
|
||||
remote_versions = get_remote_wp_versions()
|
||||
puts "#{remote_versions.size} remote versions number retrieved"
|
||||
|
||||
remote_versions.each do |version|
|
||||
unless Version.first(number: version)
|
||||
db_version = Version.create(number: version)
|
||||
version_dir = "/tmp/wordpress/"
|
||||
|
||||
puts "Downloading and unziping v#{version} to #{version_dir}"
|
||||
download_and_unzip_version(version, '/tmp/')
|
||||
|
||||
puts 'Processing Fingerprints'
|
||||
Dir[File.join(version_dir, '**', '*')].reject { |f| f =~ /^*.php$/ || Dir.exists?(f) }.each do |filename|
|
||||
hash = Digest::MD5.file(filename).hexdigest
|
||||
file_path = filename.gsub(version_dir, '')
|
||||
db_path = Path.first_or_create(value: file_path)
|
||||
fingerprint = Fingerprint.create(path_id: db_path.id, md5_hash: hash)
|
||||
|
||||
|
||||
db_version.fingerprints << fingerprint
|
||||
end
|
||||
db_version.save
|
||||
else
|
||||
puts "Version #{version} already in DB, skipping"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if @version
|
||||
if version = Version.first(number: @version)
|
||||
repository(:default).adapter.select('SELECT md5_hash, path_id, version_id, paths.value AS path FROM fingerprints LEFT JOIN paths ON path_id = id WHERE md5_hash NOT IN (SELECT DISTINCT md5_hash FROM fingerprints WHERE version_id != ?) ORDER BY path ASC', version.id).each do |f|
|
||||
if f.version_id == version.id
|
||||
puts "#{f.md5_hash} #{f.path}"
|
||||
end
|
||||
end
|
||||
else
|
||||
puts "The version supplied: '#{@version}' is not in the database"
|
||||
end
|
||||
end
|
||||
|
||||
if @hash
|
||||
puts "Results for #{@hash}:"
|
||||
Fingerprint.order_by_version(:desc).all(md5_hash: @hash).each do |f|
|
||||
puts " #{f.version.number} #{f.path.value}"
|
||||
end
|
||||
end
|
||||
|
||||
if @file
|
||||
puts "Results for #{@file}:"
|
||||
|
||||
if path = Path.first(value: @file)
|
||||
Fingerprint.order_by_version(:desc).all(path_id: path.id).each do |f|
|
||||
puts " #{f.md5_hash} #{f.version.number}"
|
||||
end
|
||||
else
|
||||
puts 'File not found (the argument must be a relative file path. e.g: wp-admin/css/widgets.css)'
|
||||
end
|
||||
end
|
||||
|
||||
if @target_url
|
||||
uri = URI.parse(@target_url)
|
||||
|
||||
Version.all(order: [ :number.desc ]).each do |version|
|
||||
total_urls = version.fingerprints.count
|
||||
matches = 0
|
||||
percent = 0
|
||||
|
||||
version.fingerprints.each do |f|
|
||||
url = uri.merge(f.path.value).to_s
|
||||
|
||||
if web_page_md5(url) == f.md5_hash
|
||||
matches += 1
|
||||
puts "#{url} matches v#{version.number}" if @verbose
|
||||
end
|
||||
|
||||
percent = ((matches / total_urls.to_f) * 100).round(2)
|
||||
|
||||
print("Version #{version.number} [#{matches}/#{total_urls} #{percent}% matches]\r")
|
||||
end
|
||||
|
||||
puts
|
||||
|
||||
if percent == 100.0
|
||||
puts "The remote version is #{version.number}"
|
||||
exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
18
example.conf.json
Normal file
18
example.conf.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0",
|
||||
|
||||
/* Uncomment the "proxy" line to use the proxy
|
||||
SOCKS proxies (4, 4A, 5) are supported, ie : "proxy": "socks5://127.0.0.1:9000"
|
||||
If you do not specify the protocol, http will be used
|
||||
*/
|
||||
//"proxy": "127.0.0.1:3128",
|
||||
//"proxy_auth": "username:password",
|
||||
|
||||
"cache_ttl": 600, // 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled
|
||||
|
||||
"request_timeout": 2000, // 2s
|
||||
|
||||
"connect_timeout": 1000, // 1s
|
||||
|
||||
"max_threads": 20
|
||||
}
|
||||
@@ -9,30 +9,35 @@ class Browser
|
||||
include Browser::Options
|
||||
|
||||
OPTIONS = [
|
||||
:available_user_agents,
|
||||
:basic_auth,
|
||||
:cache_ttl,
|
||||
:max_threads,
|
||||
:user_agent,
|
||||
:user_agent_mode,
|
||||
:proxy,
|
||||
:proxy_auth,
|
||||
:request_timeout,
|
||||
:connect_timeout
|
||||
:connect_timeout,
|
||||
:cookie
|
||||
]
|
||||
|
||||
@@instance = nil
|
||||
|
||||
attr_reader :hydra, :config_file, :cache_dir
|
||||
attr_reader :hydra, :cache_dir
|
||||
|
||||
attr_accessor :referer, :cookie
|
||||
|
||||
# @param [ Hash ] options
|
||||
#
|
||||
# @return [ Browser ]
|
||||
def initialize(options = {})
|
||||
@config_file = options[:config_file] || CONF_DIR + '/browser.conf.json'
|
||||
@cache_dir = options[:cache_dir] || CACHE_DIR + '/browser'
|
||||
|
||||
load_config
|
||||
# sets browser defaults
|
||||
browser_defaults
|
||||
# load config file
|
||||
conf = options[:config_file]
|
||||
load_config(conf) if conf
|
||||
# overrides defaults with user supplied values (overwrite values from config)
|
||||
override_config(options)
|
||||
|
||||
unless @hydra
|
||||
@@ -61,6 +66,20 @@ class Browser
|
||||
@@instance = nil
|
||||
end
|
||||
|
||||
#
|
||||
# sets browser default values
|
||||
#
|
||||
def browser_defaults
|
||||
@max_threads = 20
|
||||
# 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled
|
||||
@cache_ttl = 600
|
||||
# 2s
|
||||
@request_timeout = 2000
|
||||
# 1s
|
||||
@connect_timeout = 1000
|
||||
@user_agent = "WPScan v#{WPSCAN_VERSION} (http://wpscan.org)"
|
||||
end
|
||||
|
||||
#
|
||||
# If an option was set but is not in the new config_file
|
||||
# it's value is kept
|
||||
@@ -69,21 +88,20 @@ class Browser
|
||||
#
|
||||
# @return [ void ]
|
||||
def load_config(config_file = nil)
|
||||
@config_file = config_file || @config_file
|
||||
|
||||
if File.symlink?(@config_file)
|
||||
if File.symlink?(config_file)
|
||||
raise '[ERROR] Config file is a symlink.'
|
||||
else
|
||||
data = JSON.parse(File.read(@config_file))
|
||||
data = JSON.parse(File.read(config_file))
|
||||
end
|
||||
|
||||
OPTIONS.each do |option|
|
||||
option_name = option.to_s
|
||||
|
||||
unless data[option_name].nil?
|
||||
self.send(:"#{option_name}=", data[option_name])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# @param [ String ] url
|
||||
@@ -101,7 +119,7 @@ class Browser
|
||||
params = Browser.append_params_header_field(
|
||||
params,
|
||||
'User-Agent',
|
||||
self.user_agent
|
||||
@user_agent
|
||||
)
|
||||
|
||||
if @proxy
|
||||
@@ -120,6 +138,7 @@ class Browser
|
||||
)
|
||||
end
|
||||
|
||||
params.merge!(referer: referer)
|
||||
params.merge!(timeout: @request_timeout) if @request_timeout
|
||||
params.merge!(connecttimeout: @connect_timeout) if @connect_timeout
|
||||
|
||||
@@ -135,6 +154,7 @@ class Browser
|
||||
|
||||
params.merge!(cookiejar: @cache_dir + '/cookie-jar')
|
||||
params.merge!(cookiefile: @cache_dir + '/cookie-jar')
|
||||
params.merge!(cookie: @cookie) if @cookie
|
||||
|
||||
params
|
||||
end
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
class Browser
|
||||
module Options
|
||||
|
||||
USER_AGENT_MODES = %w{ static semi-static random }
|
||||
|
||||
attr_accessor :available_user_agents, :cache_ttl, :request_timeout, :connect_timeout
|
||||
attr_reader :basic_auth, :user_agent_mode, :proxy, :proxy_auth
|
||||
attr_accessor :cache_ttl, :request_timeout, :connect_timeout
|
||||
attr_reader :basic_auth, :proxy, :proxy_auth
|
||||
attr_writer :user_agent
|
||||
|
||||
# Sets the Basic Authentification credentials
|
||||
@@ -41,42 +39,6 @@ class Browser
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the user_agent_mode, which can be one of the following:
|
||||
# static: The UA is defined by the user, and will be the same in each requests
|
||||
# semi-static: The UA is randomly chosen at the first request, and will not change
|
||||
# random: UA randomly chosen each request
|
||||
#
|
||||
# UA are from @available_user_agents
|
||||
#
|
||||
# @param [ String ] ua_mode
|
||||
#
|
||||
# @return [ void ]
|
||||
def user_agent_mode=(ua_mode)
|
||||
ua_mode ||= 'static'
|
||||
|
||||
if USER_AGENT_MODES.include?(ua_mode)
|
||||
@user_agent_mode = ua_mode
|
||||
# For semi-static user agent mode, the user agent has to
|
||||
# be nil the first time (it will be set with the getter)
|
||||
@user_agent = nil if ua_mode === 'semi-static'
|
||||
else
|
||||
raise "Unknow user agent mode : '#{ua_mode}'"
|
||||
end
|
||||
end
|
||||
|
||||
# @return [ String ] The user agent, according to the user_agent_mode
|
||||
def user_agent
|
||||
case @user_agent_mode
|
||||
when 'semi-static'
|
||||
unless @user_agent
|
||||
@user_agent = @available_user_agents.sample
|
||||
end
|
||||
when 'random'
|
||||
@user_agent = @available_user_agents.sample
|
||||
end
|
||||
@user_agent
|
||||
end
|
||||
|
||||
# Sets the proxy
|
||||
# Accepted format:
|
||||
# [protocol://]host:post
|
||||
|
||||
@@ -18,7 +18,7 @@ class CacheFileStore
|
||||
# YAML is Human Readable, contrary to Marshal which store in a binary format
|
||||
# Marshal does not need any "require"
|
||||
def initialize(storage_path, serializer = Marshal)
|
||||
@storage_path = File.expand_path(storage_path + '/' + storage_dir)
|
||||
@storage_path = File.expand_path(File.join(storage_path, storage_dir))
|
||||
@serializer = serializer
|
||||
|
||||
# File.directory? for ruby <= 1.9 otherwise,
|
||||
@@ -35,11 +35,9 @@ class CacheFileStore
|
||||
end
|
||||
|
||||
def read_entry(key)
|
||||
entry_file_path = get_entry_file_path(key)
|
||||
|
||||
if File.exists?(entry_file_path)
|
||||
return @serializer.load(File.read(entry_file_path))
|
||||
end
|
||||
@serializer.load(File.read(get_entry_file_path(key)))
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def write_entry(key, data_to_store, cache_ttl)
|
||||
|
||||
@@ -17,6 +17,7 @@ class WpItems < Array
|
||||
hydra = browser.hydra
|
||||
targets = targets_items(wp_target, options)
|
||||
progress_bar = progress_bar(targets.size, options)
|
||||
queue_count = 0
|
||||
exist_options = {
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
@@ -43,8 +44,16 @@ class WpItems < Array
|
||||
end
|
||||
|
||||
hydra.queue(request)
|
||||
queue_count += 1
|
||||
|
||||
if queue_count >= browser.max_threads
|
||||
hydra.run
|
||||
queue_count = 0
|
||||
puts "Sent #{browser.max_threads} requests ..." if options[:verbose]
|
||||
end
|
||||
end
|
||||
|
||||
# run the remaining requests
|
||||
hydra.run
|
||||
results.sort!
|
||||
results # can't just return results.sort because the #sort returns an array, and we want a WpItems
|
||||
@@ -71,13 +80,31 @@ class WpItems < Array
|
||||
#
|
||||
# @return [ WpItems ]
|
||||
def passive_detection(wp_target, options = {})
|
||||
results = new(wp_target)
|
||||
body = Browser.get(wp_target.url).body
|
||||
results = new(wp_target)
|
||||
# improves speed
|
||||
body = remove_base64_images_from_html(body)
|
||||
names = body.scan(passive_detection_pattern(wp_target))
|
||||
body = remove_base64_images_from_html(Browser.get(wp_target.url).body)
|
||||
page = Nokogiri::HTML(body)
|
||||
names = []
|
||||
|
||||
names.flatten.uniq.each { |name| results.add(name) }
|
||||
page.css('link,script,style').each do |tag|
|
||||
%w(href src).each do |attribute|
|
||||
attr_value = tag.attribute(attribute).to_s
|
||||
next unless attr_value
|
||||
|
||||
names << Regexp.last_match[1] if attr_value.match(attribute_pattern(wp_target))
|
||||
end
|
||||
|
||||
next unless tag.name == 'script' || tag.name == 'style'
|
||||
|
||||
code = tag.text.to_s
|
||||
next if code.empty?
|
||||
|
||||
code.scan(code_pattern(wp_target)).flatten.uniq.each do |item_name|
|
||||
names << item_name
|
||||
end
|
||||
end
|
||||
|
||||
names.uniq.each { |name| results.add(name) }
|
||||
|
||||
results.sort!
|
||||
results
|
||||
@@ -88,13 +115,29 @@ class WpItems < Array
|
||||
# @param [ WpTarget ] wp_target
|
||||
#
|
||||
# @return [ Regex ]
|
||||
def passive_detection_pattern(wp_target)
|
||||
type = self.to_s.gsub(/Wp/, '').downcase
|
||||
regex1 = %r{(?:[^=:\(]+)\s?(?:=|:|\()\s?(?:"|')[^"']+\\?/}
|
||||
regex2 = %r{\\?/}
|
||||
regex3 = %r{\\?/([^/\\"']+)\\?(?:/|"|')}
|
||||
def item_pattern(wp_target)
|
||||
type = to_s.gsub(/Wp/, '').downcase
|
||||
wp_content_dir = wp_target.wp_content_dir
|
||||
wp_content_url = wp_target.uri.merge(wp_content_dir).to_s
|
||||
|
||||
/#{regex1}#{Regexp.escape(wp_target.wp_content_dir)}#{regex2}#{Regexp.escape(type)}#{regex3}/i
|
||||
url = /#{wp_content_url.gsub(%r{\A(?:http|https)}, 'https?').gsub('/', '\\\\\?\/')}/i
|
||||
content_dir = %r{(?:#{url}|\\?\/\\?\/?#{wp_content_dir})}i
|
||||
|
||||
%r{#{content_dir}\\?/#{type}\\?/}
|
||||
end
|
||||
|
||||
# @param [ WpTarget ] wp_target
|
||||
#
|
||||
# @return [ Regex ]
|
||||
def attribute_pattern(wp_target)
|
||||
/\A#{item_pattern(wp_target)}([^\/]+)/i
|
||||
end
|
||||
|
||||
# @param [ WpTarget ] wp_target
|
||||
#
|
||||
# @return [ Regex ]
|
||||
def code_pattern(wp_target)
|
||||
/["'\(]#{item_pattern(wp_target)}([^\\\/\)"']+)/i
|
||||
end
|
||||
|
||||
# The default request parameters
|
||||
@@ -133,16 +176,17 @@ class WpItems < Array
|
||||
# @return [ Array<WpItem> ]
|
||||
def vulnerable_targets_items(wp_target, item_class, vulns_file)
|
||||
targets = []
|
||||
xml = xml(vulns_file)
|
||||
json = json(vulns_file)
|
||||
|
||||
xml.xpath(item_xpath).each do |node|
|
||||
[*json].each do |item|
|
||||
targets << create_item(
|
||||
item_class,
|
||||
node.attribute('name').text,
|
||||
item.keys.inject,
|
||||
wp_target,
|
||||
vulns_file
|
||||
)
|
||||
end
|
||||
|
||||
targets
|
||||
end
|
||||
|
||||
@@ -181,6 +225,7 @@ class WpItems < Array
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
targets
|
||||
end
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ class WpPlugins < WpItems
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def item_xpath
|
||||
'//plugin'
|
||||
end
|
||||
# def item_xpath
|
||||
# '//plugin'
|
||||
# end
|
||||
|
||||
# @param [ WpTarget ] wp_target
|
||||
# @param [ Hash ] options
|
||||
@@ -68,6 +68,10 @@ class WpPlugins < WpItems
|
||||
wp_plugins.add('all-in-one-seo-pack', version: $1)
|
||||
end
|
||||
|
||||
if body =~ /<!-- This site is optimized with the Yoast WordPress SEO plugin v([^\s]+) -/i
|
||||
wp_plugins.add('wordpress-seo', version: $1)
|
||||
end
|
||||
|
||||
wp_plugins
|
||||
end
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ class WpThemes < WpItems
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def item_xpath
|
||||
'//theme'
|
||||
end
|
||||
# def item_xpath
|
||||
# '//theme'
|
||||
# end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
LIB_DIR = File.expand_path(File.dirname(__FILE__) + '/..')
|
||||
ROOT_DIR = File.expand_path(LIB_DIR + '/..') # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
||||
DATA_DIR = ROOT_DIR + '/data'
|
||||
CONF_DIR = ROOT_DIR + '/conf'
|
||||
CACHE_DIR = ROOT_DIR + '/cache'
|
||||
WPSCAN_LIB_DIR = LIB_DIR + '/wpscan'
|
||||
WPSTOOLS_LIB_DIR = LIB_DIR + '/wpstools'
|
||||
UPDATER_LIB_DIR = LIB_DIR + '/updater'
|
||||
COMMON_LIB_DIR = LIB_DIR + '/common'
|
||||
MODELS_LIB_DIR = COMMON_LIB_DIR + '/models'
|
||||
COLLECTIONS_LIB_DIR = COMMON_LIB_DIR + '/collections'
|
||||
LIB_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
||||
ROOT_DIR = File.expand_path(File.join(LIB_DIR, '..')) # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
||||
DATA_DIR = File.join(ROOT_DIR, 'data')
|
||||
CONF_DIR = File.join(ROOT_DIR, 'conf')
|
||||
CACHE_DIR = File.join(ROOT_DIR, 'cache')
|
||||
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan')
|
||||
WPSTOOLS_LIB_DIR = File.join(LIB_DIR, 'wpstools')
|
||||
UPDATER_LIB_DIR = File.join(LIB_DIR, 'updater')
|
||||
COMMON_LIB_DIR = File.join(LIB_DIR, 'common')
|
||||
MODELS_LIB_DIR = File.join(COMMON_LIB_DIR, 'models')
|
||||
COLLECTIONS_LIB_DIR = File.join(COMMON_LIB_DIR, 'collections')
|
||||
|
||||
LOG_FILE = ROOT_DIR + '/log.txt'
|
||||
LOG_FILE = File.join(ROOT_DIR, 'log.txt')
|
||||
|
||||
# Plugins directories
|
||||
COMMON_PLUGINS_DIR = COMMON_LIB_DIR + '/plugins'
|
||||
WPSCAN_PLUGINS_DIR = WPSCAN_LIB_DIR + '/plugins' # Not used ATM
|
||||
WPSTOOLS_PLUGINS_DIR = WPSTOOLS_LIB_DIR + '/plugins'
|
||||
COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
|
||||
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
|
||||
WPSTOOLS_PLUGINS_DIR = File.join(WPSTOOLS_LIB_DIR, 'plugins')
|
||||
|
||||
# Data files
|
||||
PLUGINS_FILE = DATA_DIR + '/plugins.txt'
|
||||
PLUGINS_FULL_FILE = DATA_DIR + '/plugins_full.txt'
|
||||
PLUGINS_VULNS_FILE = DATA_DIR + '/plugin_vulns.xml'
|
||||
THEMES_FILE = DATA_DIR + '/themes.txt'
|
||||
THEMES_FULL_FILE = DATA_DIR + '/themes_full.txt'
|
||||
THEMES_VULNS_FILE = DATA_DIR + '/theme_vulns.xml'
|
||||
WP_VULNS_FILE = DATA_DIR + '/wp_vulns.xml'
|
||||
WP_VERSIONS_FILE = DATA_DIR + '/wp_versions.xml'
|
||||
LOCAL_FILES_FILE = DATA_DIR + '/local_vulnerable_files.xml'
|
||||
VULNS_XSD = DATA_DIR + '/vuln.xsd'
|
||||
WP_VERSIONS_XSD = DATA_DIR + '/wp_versions.xsd'
|
||||
LOCAL_FILES_XSD = DATA_DIR + '/local_vulnerable_files.xsd'
|
||||
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.txt')
|
||||
PLUGINS_FULL_FILE = File.join(DATA_DIR, 'plugins_full.txt')
|
||||
PLUGINS_VULNS_FILE = File.join(DATA_DIR, 'plugin_vulns.json')
|
||||
THEMES_FILE = File.join(DATA_DIR, 'themes.txt')
|
||||
THEMES_FULL_FILE = File.join(DATA_DIR, 'themes_full.txt')
|
||||
THEMES_VULNS_FILE = File.join(DATA_DIR, 'theme_vulns.json')
|
||||
WP_VULNS_FILE = File.join(DATA_DIR, 'wp_vulns.json')
|
||||
WP_VERSIONS_FILE = File.join(DATA_DIR, 'wp_versions.xml')
|
||||
LOCAL_FILES_FILE = File.join(DATA_DIR, 'local_vulnerable_files.xml')
|
||||
# VULNS_XSD = File.join(DATA_DIR, 'vuln.xsd')
|
||||
WP_VERSIONS_XSD = File.join(DATA_DIR, 'wp_versions.xsd')
|
||||
LOCAL_FILES_XSD = File.join(DATA_DIR, 'local_vulnerable_files.xsd')
|
||||
USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt')
|
||||
|
||||
WPSCAN_VERSION = '2.3'
|
||||
WPSCAN_VERSION = '2.5.1'
|
||||
|
||||
$LOAD_PATH.unshift(LIB_DIR)
|
||||
$LOAD_PATH.unshift(WPSCAN_LIB_DIR)
|
||||
@@ -53,7 +54,7 @@ require 'environment'
|
||||
def require_files_from_directory(absolute_dir_path, files_pattern = '*.rb')
|
||||
files = Dir[File.join(absolute_dir_path, files_pattern)]
|
||||
|
||||
# Files in the root dir are loaded first, then thoses in the subdirectories
|
||||
# Files in the root dir are loaded first, then those in the subdirectories
|
||||
files.sort_by { |file| [file.count("/"), file] }.each do |f|
|
||||
f = File.expand_path(f)
|
||||
#puts "require #{f}" # Used for debug
|
||||
@@ -72,18 +73,56 @@ def add_trailing_slash(url)
|
||||
url =~ /\/$/ ? url : "#{url}/"
|
||||
end
|
||||
|
||||
# loading the updater
|
||||
require_files_from_directory(UPDATER_LIB_DIR)
|
||||
@updater = UpdaterFactory.get_updater(ROOT_DIR)
|
||||
|
||||
if @updater
|
||||
REVISION = @updater.local_revision_number()
|
||||
else
|
||||
REVISION = nil
|
||||
def missing_db_file?
|
||||
DbUpdater::FILES.each do |db_file|
|
||||
return true unless File.exist?(File.join(DATA_DIR, db_file))
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def version
|
||||
REVISION ? "v#{WPSCAN_VERSION}r#{REVISION}" : "v#{WPSCAN_VERSION}"
|
||||
# Define colors
|
||||
def colorize(text, color_code)
|
||||
if $COLORSWITCH
|
||||
"#{text}"
|
||||
else
|
||||
"\e[#{color_code}m#{text}\e[0m"
|
||||
end
|
||||
end
|
||||
|
||||
def bold(text)
|
||||
colorize(text, 1)
|
||||
end
|
||||
|
||||
def red(text)
|
||||
colorize(text, 31)
|
||||
end
|
||||
|
||||
def green(text)
|
||||
colorize(text, 32)
|
||||
end
|
||||
|
||||
def amber(text)
|
||||
colorize(text, 33)
|
||||
end
|
||||
|
||||
def blue(text)
|
||||
colorize(text, 34)
|
||||
end
|
||||
|
||||
def critical(text)
|
||||
red(text)
|
||||
end
|
||||
|
||||
def warning(text)
|
||||
amber(text)
|
||||
end
|
||||
|
||||
def info(text)
|
||||
green(text)
|
||||
end
|
||||
|
||||
def notice(text)
|
||||
blue(text)
|
||||
end
|
||||
|
||||
# our 1337 banner
|
||||
@@ -97,35 +136,30 @@ def banner
|
||||
puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|'
|
||||
puts
|
||||
puts ' WordPress Security Scanner by the WPScan Team '
|
||||
if REVISION
|
||||
puts " Version #{version}"
|
||||
else
|
||||
puts " Version #{version}"
|
||||
end
|
||||
puts " Version #{WPSCAN_VERSION}"
|
||||
puts ' Sponsored by the RandomStorm Open Source Initiative'
|
||||
puts ' @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_'
|
||||
puts '_______________________________________________________________'
|
||||
puts
|
||||
end
|
||||
|
||||
def colorize(text, color_code)
|
||||
"\e[#{color_code}m#{text}\e[0m"
|
||||
end
|
||||
|
||||
def red(text)
|
||||
colorize(text, 31)
|
||||
end
|
||||
|
||||
def green(text)
|
||||
colorize(text, 32)
|
||||
end
|
||||
|
||||
def xml(file)
|
||||
Nokogiri::XML(File.open(file)) do |config|
|
||||
config.noblanks
|
||||
end
|
||||
end
|
||||
|
||||
def json(file)
|
||||
content = File.open(file).read
|
||||
|
||||
begin
|
||||
JSON.parse(content)
|
||||
rescue => e
|
||||
puts "[ERROR] In JSON file parsing #{file} #{e}"
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
def redefine_constant(constant, value)
|
||||
Object.send(:remove_const, constant)
|
||||
Object.const_set(constant, value)
|
||||
@@ -186,3 +220,26 @@ def truncate(input, size, trailing = '...')
|
||||
trailing.length >= input.length or size-trailing.length-1 >= input.length
|
||||
return "#{input[0..size-trailing.length-1]}#{trailing}"
|
||||
end
|
||||
|
||||
# Gets a random User-Agent
|
||||
#
|
||||
# @return [ String ] A random user-agent from data/user-agents.txt
|
||||
def get_random_user_agent
|
||||
user_agents = []
|
||||
f = File.open(USER_AGENTS_FILE, 'r')
|
||||
f.each_line do |line|
|
||||
# ignore comments
|
||||
next if line.empty? or line =~ /^\s*(#|\/\/)/
|
||||
user_agents << line.strip
|
||||
end
|
||||
f.close
|
||||
# return ransom user-agent
|
||||
user_agents.sample
|
||||
end
|
||||
|
||||
# Directory listing enabled on url?
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def directory_listing_enabled?(url)
|
||||
Browser.get(url.to_s).body[%r{<title>Index of}] ? true : false
|
||||
end
|
||||
|
||||
115
lib/common/db_updater.rb
Normal file
115
lib/common/db_updater.rb
Normal file
@@ -0,0 +1,115 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
# DB Updater
|
||||
class DbUpdater
|
||||
FILES = %w(
|
||||
local_vulnerable_files.xml local_vulnerable_files.xsd malwares.txt
|
||||
plugins_full.txt plugins.txt themes_full.txt themes.txt
|
||||
timthumbs.txt user-agents.txt wp_versions.xml wp_versions.xsd
|
||||
plugin_vulns.json theme_vulns.json wp_vulns.json
|
||||
)
|
||||
|
||||
attr_reader :repo_directory
|
||||
|
||||
def initialize(repo_directory)
|
||||
@repo_directory = repo_directory
|
||||
|
||||
fail "#{repo_directory} is not writable" unless \
|
||||
Pathname.new(repo_directory).writable?
|
||||
end
|
||||
|
||||
# @return [ Hash ] The params for Typhoeus::Request
|
||||
def request_params
|
||||
{
|
||||
ssl_verifyhost: 2,
|
||||
ssl_verifypeer: true
|
||||
}
|
||||
end
|
||||
|
||||
# @return [ String ] The raw file URL associated with the given filename
|
||||
def remote_file_url(filename)
|
||||
"https://raw.githubusercontent.com/wpscanteam/vulndb/master/#{filename}"
|
||||
end
|
||||
|
||||
# @return [ String ] The checksum of the associated remote filename
|
||||
def remote_file_checksum(filename)
|
||||
url = "#{remote_file_url(filename)}.sha512"
|
||||
|
||||
res = Browser.get(url, request_params)
|
||||
fail "Unable to get #{url}" unless res.code == 200
|
||||
res.body
|
||||
end
|
||||
|
||||
def local_file_path(filename)
|
||||
File.join(repo_directory, "#{filename}")
|
||||
end
|
||||
|
||||
def local_file_checksum(filename)
|
||||
Digest::SHA512.file(local_file_path(filename)).hexdigest
|
||||
end
|
||||
|
||||
def backup_file_path(filename)
|
||||
File.join(repo_directory, "#{filename}.back")
|
||||
end
|
||||
|
||||
def create_backup(filename)
|
||||
return unless File.exist?(local_file_path(filename))
|
||||
FileUtils.cp(local_file_path(filename), backup_file_path(filename))
|
||||
end
|
||||
|
||||
def restore_backup(filename)
|
||||
return unless File.exist?(backup_file_path(filename))
|
||||
FileUtils.cp(backup_file_path(filename), local_file_path(filename))
|
||||
end
|
||||
|
||||
def delete_backup(filename)
|
||||
FileUtils.rm(backup_file_path(filename))
|
||||
end
|
||||
|
||||
# @return [ String ] The checksum of the downloaded file
|
||||
def download(filename)
|
||||
file_path = local_file_path(filename)
|
||||
file_url = remote_file_url(filename)
|
||||
|
||||
res = Browser.get(file_url, request_params)
|
||||
fail "Error while downloading #{file_url}" unless res.code == 200
|
||||
File.write(file_path, res.body)
|
||||
|
||||
local_file_checksum(filename)
|
||||
end
|
||||
|
||||
def update(verbose = false)
|
||||
FILES.each do |filename|
|
||||
begin
|
||||
puts "[+] Checking #{filename}" if verbose
|
||||
db_checksum = remote_file_checksum(filename)
|
||||
|
||||
# Checking if the file needs to be updated
|
||||
if File.exist?(local_file_path(filename)) && db_checksum == local_file_checksum(filename)
|
||||
puts ' [i] Already Up-To-Date' if verbose
|
||||
next
|
||||
end
|
||||
|
||||
puts ' [i] Needs to be updated' if verbose
|
||||
create_backup(filename)
|
||||
puts ' [i] Backup Created' if verbose
|
||||
puts ' [i] Downloading new file' if verbose
|
||||
dl_checksum = download(filename)
|
||||
puts " [i] Downloaded File Checksum: #{dl_checksum}" if verbose
|
||||
|
||||
unless dl_checksum == db_checksum
|
||||
fail "#{filename}: checksums do not match"
|
||||
end
|
||||
rescue => e
|
||||
puts ' [i] Restoring Backup due to error' if verbose
|
||||
restore_backup(filename)
|
||||
raise e
|
||||
ensure
|
||||
if File.exist?(backup_file_path(filename))
|
||||
puts ' [i] Deleting Backup' if verbose
|
||||
delete_backup(filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -51,7 +51,7 @@ end
|
||||
def puts(o = '')
|
||||
# remove color for logging
|
||||
if o.respond_to?(:gsub)
|
||||
temp = o.gsub(/\e\[\d+m(.*)?\e\[0m/, '\1')
|
||||
temp = o.gsub(/\e\[\d+m/, '')
|
||||
File.open(LOG_FILE, 'a+') { |f| f.puts(temp) }
|
||||
end
|
||||
super(o)
|
||||
|
||||
@@ -35,27 +35,26 @@ class Vulnerability
|
||||
end
|
||||
# :nocov:
|
||||
|
||||
# Create the Vulnerability from the xml_node
|
||||
# Create the Vulnerability from the json_item
|
||||
#
|
||||
# @param [ Nokogiri::XML::Node ] xml_node
|
||||
# @param [ Hash ] json_item
|
||||
#
|
||||
# @return [ Vulnerability ]
|
||||
def self.load_from_xml_node(xml_node)
|
||||
def self.load_from_json_item(json_item)
|
||||
references = {}
|
||||
refs = xml_node.search('references')
|
||||
if refs
|
||||
references[:url] = refs.search('url').map(&:text)
|
||||
references[:cve] = refs.search('cve').map(&:text)
|
||||
references[:secunia] = refs.search('secunia').map(&:text)
|
||||
references[:osvdb] = refs.search('osvdb').map(&:text)
|
||||
references[:metasploit] = refs.search('metasploit').map(&:text)
|
||||
references[:exploitdb] = refs.search('exploitdb').map(&:text)
|
||||
|
||||
%w(id url cve secunia osvdb metasploit exploitdb).each do |key|
|
||||
if json_item[key]
|
||||
json_item[key] = [json_item[key]] if json_item[key].class != Array
|
||||
references[key] = json_item[key]
|
||||
end
|
||||
end
|
||||
|
||||
new(
|
||||
xml_node.search('title').text,
|
||||
xml_node.search('type').text,
|
||||
json_item['title'],
|
||||
json_item['type'],
|
||||
references,
|
||||
xml_node.search('fixed_in').text,
|
||||
json_item['fixed_in'],
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -5,17 +5,17 @@ class Vulnerability
|
||||
|
||||
# output the vulnerability
|
||||
def output(verbose = false)
|
||||
puts ' |'
|
||||
puts ' | ' + red("* Title: #{title}")
|
||||
puts
|
||||
puts "#{critical('[!]')} Title: #{title}"
|
||||
references.each do |key, urls|
|
||||
methodname = "url_#{key}"
|
||||
urls.each do |u|
|
||||
url = send(methodname, u)
|
||||
puts ' | ' + red("* Reference: #{url}") if url
|
||||
puts " Reference: #{url}" if url
|
||||
end
|
||||
end
|
||||
if !fixed_in.empty?
|
||||
puts " | * Fixed in: #{fixed_in}"
|
||||
if !fixed_in.nil?
|
||||
puts "#{notice('[i]')} Fixed in: #{fixed_in}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ class Vulnerability
|
||||
def url_metasploit(module_path)
|
||||
# remove leading slash
|
||||
module_path = module_path.sub(/^\//, '')
|
||||
"http://www.metasploit.com/modules/#{module_path}"
|
||||
"http://www.rapid7.com/db/modules/#{module_path}"
|
||||
end
|
||||
|
||||
def url_url(url)
|
||||
@@ -14,7 +14,7 @@ class Vulnerability
|
||||
end
|
||||
|
||||
def url_cve(cve)
|
||||
"http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-#{cve}"
|
||||
"http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-#{cve}"
|
||||
end
|
||||
|
||||
def url_osvdb(id)
|
||||
@@ -22,12 +22,15 @@ class Vulnerability
|
||||
end
|
||||
|
||||
def url_secunia(id)
|
||||
"http://secunia.com/advisories/#{id}"
|
||||
"https://secunia.com/advisories/#{id}"
|
||||
end
|
||||
|
||||
def url_exploitdb(id)
|
||||
"http://www.exploit-db.com/exploits/#{id}/"
|
||||
end
|
||||
|
||||
def url_id(id)
|
||||
"https://wpvulndb.com/vulnerabilities/#{id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ class WpItem
|
||||
|
||||
# @return [ Boolean ]
|
||||
def has_directory_listing?
|
||||
Browser.get(@uri.to_s).body[%r{<title>Index of}] ? true : false
|
||||
directory_listing_enabled?(@uri)
|
||||
end
|
||||
|
||||
# Discover any error_log files created by WordPress
|
||||
|
||||
@@ -6,16 +6,21 @@ class WpItem
|
||||
# @return [ Void ]
|
||||
def output(verbose = false)
|
||||
puts
|
||||
puts " | Name: #{self}" #this will also output the version number if detected
|
||||
puts " | Location: #{url}"
|
||||
puts "#{info('[+]')} Name: #{self}" #this will also output the version number if detected
|
||||
puts " | Location: #{url}"
|
||||
#puts " | WordPress: #{wordpress_url}" if wordpress_org_item?
|
||||
puts " | Readme: #{readme_url}" if has_readme?
|
||||
puts " | Changelog: #{changelog_url}" if has_changelog?
|
||||
puts " | " + red('[!]') + " Directory listing is enabled: #{url}" if has_directory_listing?
|
||||
puts " | " + red('[!]') + " An error_log file has been found: #{error_log_url}" if has_error_log?
|
||||
puts " | Readme: #{readme_url}" if has_readme?
|
||||
puts " | Changelog: #{changelog_url}" if has_changelog?
|
||||
puts "#{warning('[!]')} Directory listing is enabled: #{url}" if has_directory_listing?
|
||||
puts "#{warning('[!]')} An error_log file has been found: #{error_log_url}" if has_error_log?
|
||||
|
||||
additional_output(verbose) if respond_to?(:additional_output)
|
||||
|
||||
if version.nil? && vulnerabilities.length > 0
|
||||
puts
|
||||
puts "#{warning('[+]')} We could not determine a version so all vulnerabilities are printed out"
|
||||
end
|
||||
|
||||
vulnerabilities.output
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,22 +2,27 @@
|
||||
|
||||
class WpItem
|
||||
module Vulnerable
|
||||
attr_accessor :vulns_file, :vulns_xpath
|
||||
attr_accessor :vulns_file, :identifier
|
||||
|
||||
# Get the vulnerabilities associated to the WpItem
|
||||
# Filters out already fixed vulnerabilities
|
||||
#
|
||||
# @return [ Vulnerabilities ]
|
||||
def vulnerabilities
|
||||
xml = xml(vulns_file)
|
||||
json = json(vulns_file)
|
||||
vulnerabilities = Vulnerabilities.new
|
||||
|
||||
xml.xpath(vulns_xpath).each do |node|
|
||||
vuln = Vulnerability.load_from_xml_node(node)
|
||||
if vulnerable_to?(vuln)
|
||||
vulnerabilities << vuln
|
||||
json.each do |item|
|
||||
asset = item[identifier]
|
||||
|
||||
if asset
|
||||
asset['vulnerabilities'].each do |vulnerability|
|
||||
vulnerability = Vulnerability.load_from_json_item(vulnerability)
|
||||
vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vulnerabilities
|
||||
end
|
||||
|
||||
@@ -32,7 +37,7 @@ class WpItem
|
||||
# @return [ Boolean ]
|
||||
def vulnerable_to?(vuln)
|
||||
if version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
|
||||
unless VersionCompare::is_newer_or_same?(vuln.fixed_in, version)
|
||||
unless VersionCompare::lesser_or_equal?(vuln.fixed_in, version)
|
||||
return true
|
||||
end
|
||||
else
|
||||
@@ -41,5 +46,4 @@ class WpItem
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -12,8 +12,8 @@ class WpPlugin < WpItem
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def vulns_xpath
|
||||
"//plugin[@name='#{@name}']/vulnerability"
|
||||
def identifier
|
||||
@name
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -15,15 +15,9 @@ class WpTheme < WpItem
|
||||
include WpTheme::Output
|
||||
include WpTheme::Childtheme
|
||||
|
||||
attr_writer :style_url
|
||||
attr_accessor :referenced_url
|
||||
|
||||
def allowed_options; super << :style_url end
|
||||
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
|
||||
parse_style
|
||||
end
|
||||
def allowed_options; super << :referenced_url end
|
||||
|
||||
# Sets the @uri
|
||||
#
|
||||
@@ -36,10 +30,7 @@ class WpTheme < WpItem
|
||||
|
||||
# @return [ String ] The url to the theme stylesheet
|
||||
def style_url
|
||||
unless @style_url
|
||||
@style_url = uri.merge('style.css').to_s
|
||||
end
|
||||
@style_url
|
||||
@uri.merge('style.css').to_s
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -30,21 +30,19 @@ class WpTheme < WpItem
|
||||
response = Browser.get_and_follow_location(target_uri.to_s)
|
||||
|
||||
# https + domain is optional because of relative links
|
||||
matches = %r{(?:https?://[^"']+)?/([^/]+)/themes/([^"']+)/style.css}i.match(response.body)
|
||||
matches = /(?:https?:\/\/[^"']+)?\/([^\/]+)\/themes\/([^"'\/]+)[^"']*\/style.css/i.match(response.body)
|
||||
if matches
|
||||
return new(
|
||||
target_uri,
|
||||
{
|
||||
name: matches[2],
|
||||
style_url: matches[0],
|
||||
referenced_url: matches[0],
|
||||
wp_content_dir: matches[1]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# http://code.google.com/p/wpscan/issues/detail?id=141
|
||||
#
|
||||
# @param [ URI ] target_uri
|
||||
#
|
||||
# @return [ WpTheme ]
|
||||
|
||||
@@ -5,18 +5,21 @@ class WpTheme
|
||||
|
||||
# @return [ Void ]
|
||||
def additional_output(verbose = false)
|
||||
puts " | Style URL: #{style_url}"
|
||||
puts " | Theme Name: #@theme_name" if @theme_name
|
||||
puts " | Theme URI: #@theme_uri" if @theme_uri
|
||||
parse_style
|
||||
|
||||
theme_desc = verbose ? @theme_description : truncate(@theme_description, 100)
|
||||
puts " | Description: #{theme_desc}"
|
||||
puts " | Author: #@theme_author" if @theme_author
|
||||
puts " | Author URI: #@theme_author_uri" if @theme_author_uri
|
||||
puts " | Template: #@theme_template" if @theme_template and verbose
|
||||
puts " | License: #@theme_license" if @theme_license and verbose
|
||||
puts " | License URI: #@theme_license_uri" if @theme_license_uri and verbose
|
||||
puts " | Tags: #@theme_tags" if @theme_tags and verbose
|
||||
puts " | Text Domain: #@theme_text_domain" if @theme_text_domain and verbose
|
||||
puts " | Style URL: #{style_url}"
|
||||
puts " | Referenced style.css: #{referenced_url}" if referenced_url && referenced_url != style_url
|
||||
puts " | Theme Name: #@theme_name" if @theme_name
|
||||
puts " | Theme URI: #@theme_uri" if @theme_uri
|
||||
puts " | Description: #{theme_desc}"
|
||||
puts " | Author: #@theme_author" if @theme_author
|
||||
puts " | Author URI: #@theme_author_uri" if @theme_author_uri
|
||||
puts " | Template: #@theme_template" if @theme_template and verbose
|
||||
puts " | License: #@theme_license" if @theme_license and verbose
|
||||
puts " | License URI: #@theme_license_uri" if @theme_license_uri and verbose
|
||||
puts " | Tags: #@theme_tags" if @theme_tags and verbose
|
||||
puts " | Text Domain: #@theme_text_domain" if @theme_text_domain and verbose
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -12,9 +12,8 @@ class WpTheme < WpItem
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def vulns_xpath
|
||||
"//theme[@name='#{@name}']/vulnerability"
|
||||
def identifier
|
||||
@name
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
require 'wp_timthumb/versionable'
|
||||
require 'wp_timthumb/existable'
|
||||
require 'wp_timthumb/output'
|
||||
require 'wp_timthumb/vulnerable'
|
||||
|
||||
class WpTimthumb < WpItem
|
||||
include WpTimthumb::Versionable
|
||||
include WpTimthumb::Existable
|
||||
include WpTimthumb::Output
|
||||
include WpTimthumb::Vulnerable
|
||||
|
||||
# @param [ WpTimthumb ] other
|
||||
#
|
||||
|
||||
@@ -4,7 +4,10 @@ class WpTimthumb < WpItem
|
||||
module Output
|
||||
|
||||
def output(verbose = false)
|
||||
puts ' | ' + red('[!]') + " #{self}"
|
||||
puts
|
||||
puts "#{info('[+]')} #{self}" #this will also output the version number if detected
|
||||
|
||||
vulnerabilities.output
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
55
lib/common/models/wp_timthumb/vulnerable.rb
Normal file
55
lib/common/models/wp_timthumb/vulnerable.rb
Normal file
@@ -0,0 +1,55 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTimthumb < WpItem
|
||||
module Vulnerable
|
||||
# @return [ Vulnerabilities ]
|
||||
def vulnerabilities
|
||||
vulns = Vulnerabilities.new
|
||||
|
||||
[:check_rce_132, :check_rce_webshot].each do |method|
|
||||
vuln = self.send(method)
|
||||
|
||||
vulns << vuln if vuln
|
||||
end
|
||||
vulns
|
||||
end
|
||||
|
||||
def check_rce_132
|
||||
return rce_132_vuln unless VersionCompare.lesser_or_equal?('1.33', version)
|
||||
end
|
||||
|
||||
# Vulnerable versions : > 1.35 (or >= 2.0) and < 2.8.14
|
||||
def check_rce_webshot
|
||||
return if VersionCompare.lesser_or_equal?('2.8.14', version) || VersionCompare.lesser_or_equal?(version, '1.35')
|
||||
|
||||
response = Browser.get(uri.merge('?webshot=1&src=http://' + default_allowed_domains.sample))
|
||||
|
||||
return rce_webshot_vuln unless response.body =~ /WEBSHOT_ENABLED == true/
|
||||
end
|
||||
|
||||
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)
|
||||
def default_allowed_domains
|
||||
%w(flickr.com picasa.com img.youtube.com upload.wikimedia.org)
|
||||
end
|
||||
|
||||
# @return [ Vulnerability ] The RCE in the <= 1.32
|
||||
def rce_132_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 1.32 Remote Code Execution',
|
||||
'RCE',
|
||||
{ exploitdb: ['17602'] },
|
||||
'1.33'
|
||||
)
|
||||
end
|
||||
|
||||
# @return [ Vulnerability ] The RCE due to the WebShot in the <= 2.8.13
|
||||
def rce_webshot_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
|
||||
'RCE',
|
||||
{ url: ['http://seclists.org/fulldisclosure/2014/Jun/117'] },
|
||||
'2.8.14'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,7 +12,7 @@ class WpUser < WpItem
|
||||
# @return [ Array<Symbol> ]
|
||||
def allowed_options; [:id, :login, :display_name, :password] end
|
||||
|
||||
# @return [ URI ] The uri to the auhor page
|
||||
# @return [ URI ] The uri to the author page
|
||||
def uri
|
||||
if id
|
||||
return @uri.merge("?author=#{id}")
|
||||
@@ -54,8 +54,8 @@ class WpUser < WpItem
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
s = "#{id}"
|
||||
s += " | #{login}" if login
|
||||
s += " | #{display_name}" if display_name
|
||||
s << " | #{login}" if login
|
||||
s << " | #{display_name}" if display_name
|
||||
s
|
||||
end
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class WpUser < WpItem
|
||||
|
||||
File.open(wordlist).each do |password|
|
||||
password.chop!
|
||||
|
||||
|
||||
# A successfull login will redirect us to the redirect_to parameter
|
||||
# Generate a random one on each request
|
||||
unless redirect_url
|
||||
@@ -103,19 +103,19 @@ class WpUser < WpItem
|
||||
# @return [ Boolean ]
|
||||
def valid_password?(response, password, redirect_url, options = {})
|
||||
if response.code == 302 && response.headers_hash && response.headers_hash['Location'] == redirect_url
|
||||
progression = "#{green('[SUCCESS]')} Login : #{login} Password : #{password}\n\n"
|
||||
progression = "#{info('[SUCCESS]')} Login : #{login} Password : #{password}\n\n"
|
||||
valid = true
|
||||
elsif response.body =~ /login_error/i
|
||||
verbose = "\n Incorrect login and/or password."
|
||||
elsif response.timed_out?
|
||||
progression = "#{red('ERROR:')} Request timed out."
|
||||
progression = "#{critical('ERROR:')} Request timed out."
|
||||
elsif response.code == 0
|
||||
progression = "#{red('ERROR:')} No response from remote server. WAF/IPS?"
|
||||
progression = "#{critical('ERROR:')} No response from remote server. WAF/IPS?"
|
||||
elsif response.code.to_s =~ /^50/
|
||||
progression = "#{red('ERROR:')} Server error, try reducing the number of threads."
|
||||
progression = "#{critical('ERROR:')} Server error, try reducing the number of threads."
|
||||
else
|
||||
progression = "#{red('ERROR:')} We received an unknown response for #{password}..."
|
||||
verbose = red(" Code: #{response.code}\n Body: #{response.body}\n")
|
||||
progression = "#{critical('ERROR:')} We received an unknown response for #{password}..."
|
||||
verbose = critical(" Code: #{response.code}\n Body: #{response.body}\n")
|
||||
end
|
||||
|
||||
puts "\n " + progression if progression && options[:show_progression]
|
||||
|
||||
@@ -22,6 +22,8 @@ class WpUser < WpItem
|
||||
if response.code == 301 # login in location?
|
||||
location = response.headers_hash['Location']
|
||||
|
||||
return if location.nil? || location.empty?
|
||||
|
||||
@login = Existable.login_from_author_pattern(location)
|
||||
@display_name = Existable.display_name_from_body(
|
||||
Browser.get(location).body
|
||||
@@ -66,9 +68,12 @@ class WpUser < WpItem
|
||||
title_tag.force_encoding('UTF-8') if title_tag.encoding == Encoding::ASCII_8BIT
|
||||
title_tag = Nokogiri::HTML::DocumentFragment.parse(title_tag).to_s
|
||||
# & are not decoded with Nokogiri
|
||||
title_tag.sub!('&', '&')
|
||||
title_tag.gsub!('&', '&')
|
||||
|
||||
name = title_tag[%r{([^|«]+) }, 1]
|
||||
# replace UTF chars like » with dummy character
|
||||
title_tag.gsub!(/&#(\d+);/, '|')
|
||||
|
||||
name = title_tag[%r{([^|«»]+) }, 1]
|
||||
|
||||
return name.strip if name
|
||||
end
|
||||
|
||||
@@ -190,8 +190,6 @@ class WpVersion < WpItem
|
||||
|
||||
# Attempts to find the WordPress version from the sitemap.xml file.
|
||||
#
|
||||
# See: http://code.google.com/p/wpscan/issues/detail?id=109
|
||||
#
|
||||
# @param [ URI ] target_uri
|
||||
#
|
||||
# @return [ String ] The version number
|
||||
|
||||
@@ -5,12 +5,12 @@ class WpVersion < WpItem
|
||||
|
||||
def output(verbose = false)
|
||||
puts
|
||||
puts green('[+]') + " WordPress version #{self.number} identified from #{self.found_from}"
|
||||
puts "#{info('[+]')} WordPress version #{self.number} identified from #{self.found_from}"
|
||||
|
||||
vulnerabilities = self.vulnerabilities
|
||||
|
||||
unless vulnerabilities.empty?
|
||||
puts red('[!]') + " #{vulnerabilities.size} vulnerabilities identified from the version number"
|
||||
puts "#{critical('[!]')} #{vulnerabilities.size} vulnerabilities identified from the version number"
|
||||
|
||||
vulnerabilities.output
|
||||
end
|
||||
|
||||
@@ -12,9 +12,14 @@ class WpVersion < WpItem
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def vulns_xpath
|
||||
"//wordpress[@version='#{@number}']/vulnerability"
|
||||
end
|
||||
def identifier
|
||||
@number
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
# def vulns_xpath
|
||||
# "//wordpress[@version='#{@number}']/vulnerability"
|
||||
# end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,25 +2,14 @@
|
||||
|
||||
require 'common/cache_file_store'
|
||||
|
||||
# Implementaion of a cache_key (Typhoeus::Request#hash has too many options)
|
||||
module Typhoeus
|
||||
class Request
|
||||
module Cacheable
|
||||
def cache_key
|
||||
Digest::SHA2.hexdigest("#{url}-#{options[:body]}-#{options[:method]}")[0..32]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TyphoeusCache < CacheFileStore
|
||||
|
||||
def get(request)
|
||||
read_entry(request.cache_key)
|
||||
read_entry(request.hash.to_s)
|
||||
end
|
||||
|
||||
def set(request, response)
|
||||
write_entry(request.cache_key, response, request.cache_ttl)
|
||||
write_entry(request.hash.to_s, response, request.cache_ttl)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/updater/updater'
|
||||
|
||||
class GitUpdater < Updater
|
||||
|
||||
def is_installed?
|
||||
%x[git #{repo_directory_arguments()} status 2>&1] =~ /On branch/ ? true : false
|
||||
end
|
||||
|
||||
# Git has not a revsion number like SVN,
|
||||
# so we will take the 7 first chars of the last commit hash
|
||||
def local_revision_number
|
||||
git_log = %x[git #{repo_directory_arguments()} log -1 2>&1]
|
||||
git_log[/commit ([0-9a-z]{7})/i, 1].to_s
|
||||
end
|
||||
|
||||
def update
|
||||
%x[git #{repo_directory_arguments()} pull]
|
||||
end
|
||||
|
||||
def has_local_changes?
|
||||
%x[git #{repo_directory_arguments()} diff --exit-code 2>&1] =~ /diff/ ? true : false
|
||||
end
|
||||
|
||||
def reset_head
|
||||
%x[git #{repo_directory_arguments()} reset --hard HEAD]
|
||||
end
|
||||
|
||||
protected
|
||||
def repo_directory_arguments
|
||||
if @repo_directory
|
||||
return "--git-dir=\"#{@repo_directory}/.git\" --work-tree=\"#{@repo_directory}\""
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,23 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/updater/updater'
|
||||
|
||||
class SvnUpdater < Updater
|
||||
|
||||
REVISION_PATTERN = /revision="(\d+)"/i
|
||||
TRUNK_URL = 'https://github.com/wpscanteam/wpscan'
|
||||
|
||||
def is_installed?
|
||||
%x[svn info "#@repo_directory" --xml 2>&1] =~ /revision=/ ? true : false
|
||||
end
|
||||
|
||||
def local_revision_number
|
||||
local_revision = %x[svn info "#@repo_directory" --xml 2>&1]
|
||||
local_revision[REVISION_PATTERN, 1].to_s
|
||||
end
|
||||
|
||||
def update
|
||||
%x[svn up "#@repo_directory"]
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,25 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
# This class act as an absract one
|
||||
class Updater
|
||||
|
||||
attr_reader :repo_directory
|
||||
|
||||
# TODO : add a last '/ to repo_directory if it's not present
|
||||
def initialize(repo_directory = nil)
|
||||
@repo_directory = repo_directory
|
||||
end
|
||||
|
||||
def is_installed?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def local_revision_number
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def update
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,23 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class UpdaterFactory
|
||||
|
||||
def self.get_updater(repo_directory)
|
||||
self.available_updaters_classes().each do |updater_symbol|
|
||||
updater = Object.const_get(updater_symbol).new(repo_directory)
|
||||
|
||||
if updater.is_installed?
|
||||
return updater
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# return array of class symbols
|
||||
def self.available_updaters_classes
|
||||
Object.constants.grep(/^.+Updater$/)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,14 +2,18 @@
|
||||
|
||||
class VersionCompare
|
||||
|
||||
# Compares two version strings. Returns true if version1 is equal to version2
|
||||
# or when version1 is older than version2
|
||||
# Compares two version strings. Returns true if version1 <= version2
|
||||
# and false otherwise
|
||||
#
|
||||
# @param [ String ] version1
|
||||
# @param [ String ] version2
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def self.is_newer_or_same?(version1, version2)
|
||||
def self.lesser_or_equal?(version1, version2)
|
||||
# Prepend a '0' if the version starts with a '.'
|
||||
version1 = "0#{version1}" if version1 && version1[0,1] == '.'
|
||||
version2 = "0#{version2}" if version2 && version2[0,1] == '.'
|
||||
|
||||
return true if (version1 == version2)
|
||||
# Both versions must be set
|
||||
return false unless (version1 and version2)
|
||||
|
||||
@@ -28,6 +28,7 @@ begin
|
||||
require 'pp'
|
||||
require 'shellwords'
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
# Third party libs
|
||||
require 'typhoeus'
|
||||
require 'json'
|
||||
|
||||
@@ -32,7 +32,7 @@ class WebSite
|
||||
|
||||
def has_xml_rpc?
|
||||
response = Browser.get_and_follow_location(xml_rpc_url)
|
||||
response.body =~ %r{XML-RPC server accepts POST requests only}i
|
||||
response.body =~ %r{XML-RPC server accepts POST requests only}i
|
||||
end
|
||||
|
||||
# See http://www.hixie.ch/specs/pingback/pingback-1.0#TOC2.3
|
||||
@@ -52,8 +52,12 @@ class WebSite
|
||||
url ||= @uri.to_s
|
||||
response = Browser.get(url)
|
||||
|
||||
redirected_uri = URI.parse(add_trailing_slash(add_http_protocol(url)))
|
||||
if response.code == 301 || response.code == 302
|
||||
redirection = response.headers_hash['location']
|
||||
if redirection[0] == '/'
|
||||
redirection = "#{redirected_uri.scheme}://#{redirected_uri.host}#{redirection}"
|
||||
end
|
||||
|
||||
# Let's check if there is a redirection in the redirection
|
||||
if other_redirection = redirection(redirection)
|
||||
@@ -71,7 +75,7 @@ class WebSite
|
||||
#
|
||||
# @return [ String ] The MD5 hash of the page
|
||||
def self.page_hash(page)
|
||||
page = Browser.get(page) unless page.is_a?(Typhoeus::Response)
|
||||
page = Browser.get(page, { followlocation: true, cache_ttl: 0 }) unless page.is_a?(Typhoeus::Response)
|
||||
|
||||
Digest::MD5.hexdigest(page.body.gsub(/<!--.*?-->/m, ''))
|
||||
end
|
||||
|
||||
@@ -12,9 +12,7 @@ class WebSite
|
||||
# Gets a robots.txt URL
|
||||
# @return [ String ]
|
||||
def robots_url
|
||||
temp = @uri.clone
|
||||
temp.path = '/robots.txt'
|
||||
temp.to_s
|
||||
@uri.clone.merge('robots.txt').to_s
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ require 'wp_target/malwares'
|
||||
require 'wp_target/wp_readme'
|
||||
require 'wp_target/wp_registrable'
|
||||
require 'wp_target/wp_config_backup'
|
||||
require 'wp_target/wp_must_use_plugins'
|
||||
require 'wp_target/wp_login_protection'
|
||||
require 'wp_target/wp_custom_directories'
|
||||
require 'wp_target/wp_full_path_disclosure'
|
||||
@@ -14,6 +15,7 @@ class WpTarget < WebSite
|
||||
include WpTarget::WpReadme
|
||||
include WpTarget::WpRegistrable
|
||||
include WpTarget::WpConfigBackup
|
||||
include WpTarget::WpMustUsePlugins
|
||||
include WpTarget::WpLoginProtection
|
||||
include WpTarget::WpCustomDirectories
|
||||
include WpTarget::WpFullPathDisclosure
|
||||
@@ -28,7 +30,7 @@ class WpTarget < WebSite
|
||||
@wp_plugins_dir = options[:wp_plugins_dir]
|
||||
@multisite = nil
|
||||
|
||||
Browser.instance(options.merge(:max_threads => options[:threads]))
|
||||
Browser.instance.referer = url
|
||||
end
|
||||
|
||||
# check if the target website is
|
||||
@@ -38,6 +40,11 @@ class WpTarget < WebSite
|
||||
|
||||
response = Browser.get_and_follow_location(@uri.to_s)
|
||||
|
||||
# Note: in the future major WPScan version, change the user-agent to see
|
||||
# if the response is a 200 ?
|
||||
fail "The target is responding with a 403, this might be due to a WAF or a plugin\n" \
|
||||
'You should try to supply a valid user-agent via the --user-agent option' if response.code == 403
|
||||
|
||||
if response.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i
|
||||
wordpress = true
|
||||
else
|
||||
@@ -93,7 +100,7 @@ class WpTarget < WebSite
|
||||
end
|
||||
# :nocov:
|
||||
|
||||
# The version is not yet considerated
|
||||
# The version is not yet considered
|
||||
#
|
||||
# @param [ String ] name
|
||||
# @param [ String ] version
|
||||
@@ -116,7 +123,12 @@ class WpTarget < WebSite
|
||||
|
||||
# @return [ String ]
|
||||
def debug_log_url
|
||||
@uri.merge("#{wp_content_dir()}/debug.log").to_s
|
||||
@uri.merge("#{wp_content_dir}/debug.log").to_s
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def upload_dir_url
|
||||
@uri.merge("#{wp_content_dir}/uploads/").to_s
|
||||
end
|
||||
|
||||
# Script for replacing strings in wordpress databases
|
||||
@@ -133,4 +145,8 @@ class WpTarget < WebSite
|
||||
resp = Browser.get(search_replace_db_2_url)
|
||||
resp.code == 200 && resp.body[%r{by interconnect}i]
|
||||
end
|
||||
|
||||
def upload_directory_listing_enabled?
|
||||
directory_listing_enabled?(upload_dir_url)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,9 +40,9 @@ class WpTarget < WebSite
|
||||
# @return [ Array ]
|
||||
def self.config_backup_files
|
||||
%w{
|
||||
wp-config.php~ #wp-config.php# wp-config.php.save wp-config.php.swp wp-config.php.swo wp-config.php_bak
|
||||
wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old wp-config.php.orig
|
||||
wp-config.orig wp-config.php.original wp-config.original wp-config.txt
|
||||
wp-config.php~ #wp-config.php# wp-config.php.save .wp-config.php.swp wp-config.php.swp wp-config.php.swo
|
||||
wp-config.php_bak wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old
|
||||
wp-config.php.orig wp-config.orig wp-config.php.original wp-config.original wp-config.txt
|
||||
} # thanks to Feross.org for these
|
||||
end
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ class WpTarget < WebSite
|
||||
end
|
||||
|
||||
# Checks if a login protection plugin is enabled
|
||||
# http://code.google.com/p/wpscan/issues/detail?id=111
|
||||
# return a WpPlugin object or nil if no one is found
|
||||
def login_protection_plugin
|
||||
unless @login_protection_plugin
|
||||
|
||||
26
lib/wpscan/wp_target/wp_must_use_plugins.rb
Normal file
26
lib/wpscan/wp_target/wp_must_use_plugins.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTarget < WebSite
|
||||
module WpMustUsePlugins
|
||||
|
||||
# Checks to see if the must use plugin folder exists
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def has_must_use_plugins?
|
||||
response = Browser.get(must_use_url)
|
||||
|
||||
if response && WpTarget.valid_response_codes.include?(response.code)
|
||||
hash = WebSite.page_hash(response.body)
|
||||
return true if hash != error_404_hash && hash != homepage_hash
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# @return [ String ] The must use plugins directory URL
|
||||
def must_use_url
|
||||
@uri.merge("#{wp_content_dir}/mu-plugins/").to_s
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -46,7 +46,7 @@ def usage
|
||||
puts '-Use custom plugins directory ...'
|
||||
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
||||
puts
|
||||
puts '-Update ...'
|
||||
puts '-Update the DB ...'
|
||||
puts "ruby #{script_name} --update"
|
||||
puts
|
||||
puts '-Debug output ...'
|
||||
@@ -60,13 +60,12 @@ end
|
||||
def help
|
||||
puts 'Help :'
|
||||
puts
|
||||
puts 'Some values are settable in conf/browser.conf.json :'
|
||||
puts ' user-agent, proxy, proxy-auth, threads, cache timeout and request timeout'
|
||||
puts 'Some values are settable in a config file, see the example.conf.json'
|
||||
puts
|
||||
puts '--update Update to the latest revision'
|
||||
puts '--url | -u <target url> The WordPress URL/domain to scan.'
|
||||
puts '--force | -f Forces WPScan to not check if the remote site is running WordPress.'
|
||||
puts '--enumerate | -e [option(s)] Enumeration.'
|
||||
puts '--update Update to the database to the latest version.'
|
||||
puts '--url | -u <target url> The WordPress URL/domain to scan.'
|
||||
puts '--force | -f Forces WPScan to not check if the remote site is running WordPress.'
|
||||
puts '--enumerate | -e [option(s)] Enumeration.'
|
||||
puts ' option :'
|
||||
puts ' u usernames from id 1 to 10'
|
||||
puts ' u[10-20] usernames from id 10 to 20 (you must write [] chars)'
|
||||
@@ -80,20 +79,40 @@ def help
|
||||
puts ' Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins'
|
||||
puts ' If no option is supplied, the default is "vt,tt,u,vp"'
|
||||
puts
|
||||
puts '--exclude-content-based "<regexp or string>" Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied'
|
||||
puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)'
|
||||
puts '--config-file | -c <config file> Use the specified config file'
|
||||
puts '--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not'
|
||||
puts '--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it. Subdirectories are allowed'
|
||||
puts '--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed'
|
||||
puts '--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).'
|
||||
puts ' HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used'
|
||||
puts '--proxy-auth <username:password> Supply the proxy login credentials (will override the one from conf/browser.conf.json).'
|
||||
puts '--basic-auth <username:password> Set the HTTP Basic authentication'
|
||||
puts '--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute.'
|
||||
puts '--threads | -t <number of threads> The number of threads to use when multi-threading requests. (will override the value from conf/browser.conf.json)'
|
||||
puts '--username | -U <username> Only brute force the supplied username.'
|
||||
puts '--help | -h This help screen.'
|
||||
puts '--verbose | -v Verbose output.'
|
||||
puts '--exclude-content-based "<regexp or string>"'
|
||||
puts ' Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied.'
|
||||
puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).'
|
||||
puts '--config-file | -c <config file> Use the specified config file, see the example.conf.json.'
|
||||
puts '--user-agent | -a <User-Agent> Use the specified User-Agent.'
|
||||
puts '--cookie <String> String to read cookies from.'
|
||||
puts '--random-agent | -r Use a random User-Agent.'
|
||||
puts '--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not'
|
||||
puts '--batch Never ask for user input, use the default behaviour.'
|
||||
puts '--no-color Do not use colors in the output.'
|
||||
puts '--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it.'
|
||||
puts ' Subdirectories are allowed.'
|
||||
puts '--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory.'
|
||||
puts ' If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed'
|
||||
puts '--proxy <[protocol://]host:port> Supply a proxy. HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported.'
|
||||
puts ' If no protocol is given (format host:port), HTTP will be used.'
|
||||
puts '--proxy-auth <username:password> Supply the proxy login credentials.'
|
||||
puts '--basic-auth <username:password> Set the HTTP Basic authentication.'
|
||||
puts '--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute.'
|
||||
puts '--username | -U <username> Only brute force the supplied username.'
|
||||
puts '--threads | -t <number of threads> The number of threads to use when multi-threading requests.'
|
||||
puts '--cache-ttl <cache-ttl> Typhoeus cache TTL.'
|
||||
puts '--request-timeout <request-timeout> Request Timeout.'
|
||||
puts '--connect-timeout <connect-timeout> Connect Timeout.'
|
||||
puts '--max-threads <max-threads> Maximum Threads.'
|
||||
puts '--help | -h This help screen.'
|
||||
puts '--verbose | -v Verbose output.'
|
||||
puts
|
||||
end
|
||||
|
||||
# Hook to check if the target if down during the scan
|
||||
# The target is considered down after 10 requests with status = 0
|
||||
down = 0
|
||||
Typhoeus.on_complete do |response|
|
||||
down += 1 if response.code == 0
|
||||
fail 'The target seems to be down' if down >= 10
|
||||
end
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
class WpscanOptions
|
||||
|
||||
ACCESSOR_OPTIONS = [
|
||||
:batch,
|
||||
:enumerate_plugins,
|
||||
:enumerate_only_vulnerable_plugins,
|
||||
:enumerate_all_plugins,
|
||||
@@ -12,6 +13,7 @@ class WpscanOptions
|
||||
:enumerate_timthumbs,
|
||||
:enumerate_usernames,
|
||||
:enumerate_usernames_range,
|
||||
:no_color,
|
||||
:proxy,
|
||||
:proxy_auth,
|
||||
:threads,
|
||||
@@ -27,10 +29,17 @@ class WpscanOptions
|
||||
:wp_plugins_dir,
|
||||
:help,
|
||||
:config_file,
|
||||
:cookie,
|
||||
:exclude_content_based,
|
||||
:basic_auth,
|
||||
:debug_output,
|
||||
:version
|
||||
:version,
|
||||
:user_agent,
|
||||
:random_agent,
|
||||
:cache_ttl,
|
||||
:request_timeout,
|
||||
:connect_timeout,
|
||||
:max_threads
|
||||
]
|
||||
|
||||
attr_accessor *ACCESSOR_OPTIONS
|
||||
@@ -136,6 +145,10 @@ class WpscanOptions
|
||||
!to_h.empty?
|
||||
end
|
||||
|
||||
def random_agent=(useless)
|
||||
@user_agent = get_random_user_agent
|
||||
end
|
||||
|
||||
# return Hash
|
||||
def to_h
|
||||
options = {}
|
||||
@@ -227,6 +240,8 @@ class WpscanOptions
|
||||
['--wordlist', '-w', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--threads', '-t', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--force', '-f', GetoptLong::NO_ARGUMENT],
|
||||
['--user-agent', '-a', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--random-agent', '-r', GetoptLong::NO_ARGUMENT],
|
||||
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
||||
['--verbose', '-v', GetoptLong::NO_ARGUMENT],
|
||||
['--proxy', GetoptLong::REQUIRED_ARGUMENT],
|
||||
@@ -239,7 +254,14 @@ class WpscanOptions
|
||||
['--exclude-content-based', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--basic-auth', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--debug-output', GetoptLong::NO_ARGUMENT],
|
||||
['--version', GetoptLong::NO_ARGUMENT]
|
||||
['--version', GetoptLong::NO_ARGUMENT],
|
||||
['--cache-ttl', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--request-timeout', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--connect-timeout', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--max-threads', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--batch', GetoptLong::NO_ARGUMENT],
|
||||
['--no-color', GetoptLong::NO_ARGUMENT],
|
||||
['--cookie', GetoptLong::REQUIRED_ARGUMENT]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -29,13 +29,22 @@ class CheckerPlugin < Plugin
|
||||
puts '[+] Checking vulnerabilities reference urls'
|
||||
|
||||
vuln_ref_files.each do |vuln_ref_file|
|
||||
xml = xml(vuln_ref_file)
|
||||
json = json(vuln_ref_file)
|
||||
|
||||
urls = []
|
||||
xml.xpath('//reference').each { |node| urls << node.text }
|
||||
|
||||
json.each do |asset|
|
||||
asset[asset.keys.inject]['vulnerabilities'].each do |url|
|
||||
unless url['url'].nil?
|
||||
url['url'].each do |url|
|
||||
urls << url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
urls.uniq!
|
||||
|
||||
puts "[!] No URLs found in #{vuln_ref_file}!" if urls.empty?
|
||||
|
||||
dead_urls = []
|
||||
queue_count = 0
|
||||
request_count = 0
|
||||
@@ -73,17 +82,19 @@ class CheckerPlugin < Plugin
|
||||
end
|
||||
|
||||
def check_local_vulnerable_files(dir_to_scan)
|
||||
if Dir::exist?(dir_to_scan)
|
||||
if Dir.exist?(dir_to_scan)
|
||||
xml_file = LOCAL_FILES_FILE
|
||||
local_hashes = {}
|
||||
file_extension_to_scan = '*.{js,php,swf,html,htm}'
|
||||
|
||||
print '[+] Generating local hashes ... '
|
||||
|
||||
Dir[File::join(dir_to_scan, '**', file_extension_to_scan)].each do |filename|
|
||||
Dir[File.join(dir_to_scan, '**', file_extension_to_scan)]
|
||||
.select { |f| File.file?(f) }
|
||||
.each do |filename|
|
||||
sha1sum = Digest::SHA1.file(filename).hexdigest
|
||||
|
||||
if local_hashes.has_key?(sha1sum)
|
||||
if local_hashes.key?(sha1sum)
|
||||
local_hashes[sha1sum] << filename
|
||||
else
|
||||
local_hashes[sha1sum] = [filename]
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
# This tool generates a list to use for plugin and theme enumeration
|
||||
class GenerateList
|
||||
|
||||
attr_accessor :verbose
|
||||
|
||||
# type = themes | plugins
|
||||
def initialize(type, verbose)
|
||||
if type =~ /plugins/i
|
||||
@type = 'plugin'
|
||||
@svn_url = 'http://plugins.svn.wordpress.org/'
|
||||
@popular_url = 'http://wordpress.org/plugins/browse/popular/'
|
||||
@popular_regex = %r{<h3><a href="http://wordpress.org/plugins/([^/]+)/">.+</a></h3>}i
|
||||
elsif type =~ /themes/i
|
||||
@type = 'theme'
|
||||
@svn_url = 'http://themes.svn.wordpress.org/'
|
||||
@popular_url = 'http://wordpress.org/themes/browse/popular/'
|
||||
@popular_regex = %r{<h3><a href="http://wordpress.org/themes/([^/]+)">.+</a></h3>}i
|
||||
else
|
||||
raise "Type #{type} not defined"
|
||||
end
|
||||
@verbose = verbose
|
||||
@browser = Browser.instance(request_timeout: 20000, connect_timeout: 20000, max_threads: 1, cache_ttl: 0)
|
||||
end
|
||||
|
||||
def set_file_name(type)
|
||||
case @type
|
||||
when 'plugin'
|
||||
case type
|
||||
when :full
|
||||
@file_name = PLUGINS_FULL_FILE
|
||||
when :popular
|
||||
@file_name = PLUGINS_FILE
|
||||
else
|
||||
raise 'Unknown type'
|
||||
end
|
||||
when 'theme'
|
||||
case type
|
||||
when :full
|
||||
@file_name = THEMES_FULL_FILE
|
||||
when :popular
|
||||
@file_name = THEMES_FILE
|
||||
else
|
||||
raise 'Unknown type'
|
||||
end
|
||||
else
|
||||
raise "Unknown type #@type"
|
||||
end
|
||||
end
|
||||
|
||||
def generate_full_list
|
||||
set_file_name(:full)
|
||||
items = SvnParser.new(@svn_url).parse
|
||||
save items
|
||||
end
|
||||
|
||||
def generate_popular_list(pages)
|
||||
set_file_name(:popular)
|
||||
items = get_popular_items(pages)
|
||||
save items
|
||||
end
|
||||
|
||||
# Send a HTTP request to the WordPress most popular theme or plugin webpage
|
||||
# parse the response for the names.
|
||||
def get_popular_items(pages)
|
||||
found_items = []
|
||||
page_count = 1
|
||||
|
||||
(1...(pages.to_i + 1)).each do |page|
|
||||
# First page has another URL
|
||||
url = (page == 1) ? @popular_url : @popular_url + 'page/' + page.to_s + '/'
|
||||
puts "[+] Parsing page #{page_count}" if @verbose
|
||||
code = 0
|
||||
while code != 200
|
||||
puts red("[!] Retrying request for page #{page} (Code: #{code})") unless code == 0
|
||||
request = @browser.forge_request(url)
|
||||
response = request.run
|
||||
code = response.code
|
||||
sleep(5) unless code == 200
|
||||
end
|
||||
page_count += 1
|
||||
found = 0
|
||||
response.body.scan(@popular_regex).each do |item|
|
||||
found_items << item[0]
|
||||
found = found + 1
|
||||
end
|
||||
puts "[+] Found #{found} items on page #{page}" if @verbose
|
||||
end
|
||||
|
||||
found_items.sort!
|
||||
found_items.uniq
|
||||
end
|
||||
|
||||
# Save the file
|
||||
def save(items)
|
||||
items.sort!
|
||||
items.uniq!
|
||||
puts "[*] We have parsed #{items.length} #{@type}s"
|
||||
File.open(@file_name, 'w') { |f| f.puts(items) }
|
||||
puts "New #@file_name file created"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,53 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class ListGeneratorPlugin < Plugin
|
||||
|
||||
def initialize
|
||||
super(author: 'WPScanTeam - @FireFart')
|
||||
|
||||
register_options(
|
||||
['--generate-plugin-list [NUMBER_OF_PAGES]', '--gpl', Integer, 'Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)'],
|
||||
['--generate-full-plugin-list', '--gfpl', 'Generate a new full data/plugins.txt file'],
|
||||
|
||||
['--generate-theme-list [NUMBER_OF_PAGES]', '--gtl', Integer, 'Generate a new data/themes.txt file. (supply number of *pages* to parse, default : 20)'],
|
||||
['--generate-full-theme-list', '--gftl', 'Generate a new full data/themes.txt file'],
|
||||
|
||||
['--generate-all', '--ga', 'Generate a new full plugins, full themes, popular plugins and popular themes list']
|
||||
)
|
||||
end
|
||||
|
||||
def run(options = {})
|
||||
@verbose = options[:verbose] || false
|
||||
generate_all = options[:generate_all] || false
|
||||
|
||||
if options.has_key?(:generate_plugin_list) || generate_all
|
||||
most_popular('plugin', options[:generate_plugin_list] || 150)
|
||||
end
|
||||
|
||||
if options[:generate_full_plugin_list] || generate_all
|
||||
full('plugin')
|
||||
end
|
||||
|
||||
if options.has_key?(:generate_theme_list) || generate_all
|
||||
most_popular('theme', options[:generate_theme_list] || 20)
|
||||
end
|
||||
|
||||
if options[:generate_full_theme_list] || generate_all
|
||||
full('theme')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def most_popular(type, number_of_pages)
|
||||
puts "[+] Generating new most popular #{type} list"
|
||||
puts
|
||||
GenerateList.new(type + 's', @verbose).generate_popular_list(number_of_pages)
|
||||
end
|
||||
|
||||
def full(type)
|
||||
puts "[+] Generating new full #{type} list"
|
||||
puts
|
||||
GenerateList.new(type + 's', @verbose).generate_full_list
|
||||
end
|
||||
end
|
||||
@@ -1,31 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
# This Class Parses SVN Repositories via HTTP
|
||||
class SvnParser
|
||||
|
||||
attr_accessor :verbose, :svn_root, :keep_empty_dirs
|
||||
|
||||
def initialize(svn_root)
|
||||
@svn_root = svn_root
|
||||
end
|
||||
|
||||
def parse
|
||||
get_root_directories
|
||||
end
|
||||
|
||||
#Private methods start here
|
||||
private
|
||||
|
||||
# Gets all directories in the SVN root
|
||||
def get_root_directories
|
||||
dirs = []
|
||||
rootindex = Browser.get(@svn_root).body
|
||||
|
||||
rootindex.scan(%r{<li><a href=".+">(.+)/</a></li>}i).each do |dir|
|
||||
dirs << dir[0]
|
||||
end
|
||||
|
||||
dirs.sort!
|
||||
dirs.uniq
|
||||
end
|
||||
end
|
||||
@@ -6,7 +6,7 @@ class StatsPlugin < Plugin
|
||||
super(author: 'WPScanTeam - Christian Mehlmauer')
|
||||
|
||||
register_options(
|
||||
['--stats', '--s', 'Show WpScan Database statistics']
|
||||
['--stats', '-s', 'Show WpScan Database statistics.']
|
||||
)
|
||||
end
|
||||
|
||||
@@ -20,15 +20,19 @@ class StatsPlugin < Plugin
|
||||
|
||||
puts "WPScan Database Statistics:"
|
||||
puts "---------------------------"
|
||||
puts "[#] Total WordPress Sites in the World: #{get_wp_installations}"
|
||||
puts
|
||||
puts "[#] Total vulnerable versions: #{vuln_core_count}"
|
||||
puts "[#] Total vulnerable plugins: #{vuln_plugin_count}"
|
||||
puts "[#] Total vulnerable themes: #{vuln_theme_count}"
|
||||
puts
|
||||
puts "[#] Total version vulnerabilities: #{version_vulns_count}"
|
||||
puts "[#] Total fixed vulnerabilities: #{fix_version_count}"
|
||||
puts
|
||||
puts "[#] Total plugin vulnerabilities: #{plugin_vulns_count}"
|
||||
puts "[#] Total fixed vulnerabilities: #{fix_plugin_count}"
|
||||
puts
|
||||
puts "[#] Total theme vulnerabilities: #{theme_vulns_count}"
|
||||
puts "[#] Total fixed vulnerabilities: #{fix_theme_count}"
|
||||
puts
|
||||
puts "[#] Total plugins to enumerate: #{total_plugins}"
|
||||
puts "[#] Total themes to enumerate: #{total_themes}"
|
||||
@@ -44,27 +48,39 @@ class StatsPlugin < Plugin
|
||||
end
|
||||
|
||||
def vuln_core_count(file=WP_VULNS_FILE)
|
||||
xml(file).xpath('count(//wordpress)').to_i
|
||||
json(file).size
|
||||
end
|
||||
|
||||
def vuln_plugin_count(file=PLUGINS_VULNS_FILE)
|
||||
xml(file).xpath('count(//plugin)').to_i
|
||||
json(file).size
|
||||
end
|
||||
|
||||
def vuln_theme_count(file=THEMES_VULNS_FILE)
|
||||
xml(file).xpath('count(//theme)').to_i
|
||||
json(file).size
|
||||
end
|
||||
|
||||
def version_vulns_count(file=WP_VULNS_FILE)
|
||||
xml(file).xpath('count(//vulnerability)').to_i
|
||||
asset_vulns_count(json(file))
|
||||
end
|
||||
|
||||
def fix_version_count(file=WP_VULNS_FILE)
|
||||
asset_fixed_in_count(json(file))
|
||||
end
|
||||
|
||||
def plugin_vulns_count(file=PLUGINS_VULNS_FILE)
|
||||
xml(file).xpath('count(//vulnerability)').to_i
|
||||
asset_vulns_count(json(file))
|
||||
end
|
||||
|
||||
def fix_plugin_count(file=PLUGINS_VULNS_FILE)
|
||||
asset_fixed_in_count(json(file))
|
||||
end
|
||||
|
||||
def theme_vulns_count(file=THEMES_VULNS_FILE)
|
||||
xml(file).xpath('count(//vulnerability)').to_i
|
||||
asset_vulns_count(json(file))
|
||||
end
|
||||
|
||||
def fix_theme_count(file=THEMES_VULNS_FILE)
|
||||
asset_fixed_in_count(json(file))
|
||||
end
|
||||
|
||||
def total_plugins(file=PLUGINS_FULL_FILE)
|
||||
@@ -79,9 +95,12 @@ class StatsPlugin < Plugin
|
||||
IO.readlines(file).size
|
||||
end
|
||||
|
||||
def get_wp_installations()
|
||||
page = Nokogiri::HTML(Typhoeus.get('http://en.wordpress.com/stats/').body)
|
||||
page.css('span[class="stats-flipper-number"]').text
|
||||
def asset_vulns_count(json)
|
||||
json.map { |asset| asset[asset.keys.inject]['vulnerabilities'].size }.inject(:+)
|
||||
end
|
||||
|
||||
def asset_fixed_in_count(json)
|
||||
json.map { |asset| asset[asset.keys.inject]['vulnerabilities'].map {|a| a['fixed_in'].nil? ? 0 : 1 }.inject(:+) }.inject(:+)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -12,21 +12,6 @@ def usage
|
||||
puts
|
||||
puts 'Examples:'
|
||||
puts
|
||||
puts "- Generate a new 'most popular' plugin list, up to 150 pages ..."
|
||||
puts "ruby #{script_name} --generate-plugin-list 150"
|
||||
puts
|
||||
puts '- Generate a new full plugin list'
|
||||
puts "ruby #{script_name} --generate-full-plugin-list"
|
||||
puts
|
||||
puts "- Generate a new 'most popular' theme list, up to 150 pages ..."
|
||||
puts "ruby #{script_name} --generate-theme-list 150"
|
||||
puts
|
||||
puts '- Generate a new full theme list'
|
||||
puts "ruby #{script_name} --generate-full-theme-list"
|
||||
puts
|
||||
puts '- Generate all list'
|
||||
puts "ruby #{script_name} --generate-all"
|
||||
puts
|
||||
puts 'Locally scan a wordpress installation for vulnerable files or shells'
|
||||
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
|
||||
puts
|
||||
|
||||
@@ -6,9 +6,9 @@ describe Browser do
|
||||
it_behaves_like 'Browser::Actions'
|
||||
it_behaves_like 'Browser::Options'
|
||||
|
||||
CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json'
|
||||
CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy.json'
|
||||
#CONFIG_FILE_WITH_PROXY_AND_AUTH = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy_auth.json'
|
||||
CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser.conf.json'
|
||||
CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser.conf_proxy.json'
|
||||
#CONFIG_FILE_WITH_PROXY_AND_AUTH = SPEC_FIXTURES_CONF_DIR + '/browser.conf_proxy_auth.json'
|
||||
|
||||
subject(:browser) {
|
||||
Browser.reset
|
||||
@@ -16,17 +16,16 @@ describe Browser do
|
||||
}
|
||||
let(:options) { {} }
|
||||
let(:instance_vars_to_check) {
|
||||
['user_agent', 'user_agent_mode', 'available_user_agents', 'proxy',
|
||||
'max_threads', 'cache_ttl', 'request_timeout', 'connect_timeout']
|
||||
['proxy', 'max_threads', 'cache_ttl', 'request_timeout', 'connect_timeout']
|
||||
}
|
||||
let(:json_config_without_proxy) { JSON.parse(File.read(CONFIG_FILE_WITHOUT_PROXY)) }
|
||||
let(:json_config_with_proxy) { JSON.parse(File.read(CONFIG_FILE_WITH_PROXY)) }
|
||||
|
||||
def check_instance_variables(browser, json_expected_vars)
|
||||
json_expected_vars['max_threads'] ||= 1 # max_thread can not be nil
|
||||
json_expected_vars['max_threads'] ||= 20 # max_thread can not be nil
|
||||
|
||||
instance_vars_to_check.each do |variable_name|
|
||||
browser.send(:"#{variable_name}").should === json_expected_vars[variable_name]
|
||||
expect(browser.send(:"#{variable_name}")).to be === json_expected_vars[variable_name]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,12 +38,6 @@ describe Browser do
|
||||
describe '::instance' do
|
||||
after { check_instance_variables(browser, @json_expected_vars) }
|
||||
|
||||
context "when default config_file = #{CONFIG_FILE_WITHOUT_PROXY}" do
|
||||
it 'will check the instance vars' do
|
||||
@json_expected_vars = json_config_without_proxy
|
||||
end
|
||||
end
|
||||
|
||||
context "when :config_file = #{CONFIG_FILE_WITH_PROXY}" do
|
||||
let(:options) { { config_file: CONFIG_FILE_WITH_PROXY } }
|
||||
|
||||
@@ -57,7 +50,7 @@ describe Browser do
|
||||
let(:cache_dir) { CACHE_DIR + '/somewhere' }
|
||||
let(:options) { { cache_dir: cache_dir } }
|
||||
|
||||
after { subject.cache_dir.should == cache_dir }
|
||||
after { expect(subject.cache_dir).to eq cache_dir }
|
||||
|
||||
it 'sets @cache_dir' do
|
||||
@json_expected_vars = json_config_without_proxy
|
||||
@@ -91,11 +84,11 @@ describe Browser do
|
||||
|
||||
describe '::append_params_header_field' do
|
||||
after :each do
|
||||
Browser.append_params_header_field(
|
||||
expect(Browser.append_params_header_field(
|
||||
@params,
|
||||
@field,
|
||||
@field_value
|
||||
).should === @expected
|
||||
)).to be === @expected
|
||||
end
|
||||
|
||||
context 'when there is no headers' do
|
||||
@@ -138,15 +131,16 @@ describe Browser do
|
||||
ssl_verifypeer: false, ssl_verifyhost: 0,
|
||||
cookiejar: cookie_jar, cookiefile: cookie_jar,
|
||||
timeout: 2000, connecttimeout: 1000,
|
||||
maxredirs: 3
|
||||
maxredirs: 3,
|
||||
referer: nil
|
||||
}
|
||||
}
|
||||
|
||||
after :each do
|
||||
browser.stub(user_agent: 'SomeUA')
|
||||
browser.user_agent = 'SomeUA'
|
||||
browser.cache_ttl = 250
|
||||
|
||||
browser.merge_request_params(params).should == @expected
|
||||
expect(browser.merge_request_params(params)).to eq @expected
|
||||
end
|
||||
|
||||
it 'sets the User-Agent header field and cache_ttl' do
|
||||
@@ -196,18 +190,27 @@ describe Browser do
|
||||
@expected = default_expectation.merge(params)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when @cookie' do
|
||||
let(:cookie) { 'foor=bar;bar=foo' }
|
||||
before { browser.cookie = cookie }
|
||||
|
||||
it 'sets the cookie' do
|
||||
@expected = default_expectation.merge(cookie: cookie)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#forge_request' do
|
||||
let(:url) { 'http://example.localhost' }
|
||||
|
||||
it 'returns the correct Typhoeus::Request' do
|
||||
subject.stub(merge_request_params: { cache_ttl: 10 })
|
||||
allow(subject).to receive_messages(merge_request_params: { cache_ttl: 10 })
|
||||
|
||||
request = subject.forge_request(url)
|
||||
request.should be_a Typhoeus::Request
|
||||
request.url.should == url
|
||||
request.cache_ttl.should == 10
|
||||
expect(request).to be_a Typhoeus::Request
|
||||
expect(request.url).to eq url
|
||||
expect(request.cache_ttl).to eq 10
|
||||
end
|
||||
|
||||
end
|
||||
@@ -222,7 +225,7 @@ describe Browser do
|
||||
response1 = Browser.get(url)
|
||||
response2 = Browser.get(url)
|
||||
|
||||
response1.body.should == response2.body
|
||||
expect(response1.body).to eq response2.body
|
||||
#WebMock.should have_requested(:get, url).times(1) # This one fail, dunno why :s (but it works without mock)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,14 +17,14 @@ describe CacheFileStore do
|
||||
|
||||
describe '#storage_path' do
|
||||
it 'returns the storage path given in the #new' do
|
||||
@cache.storage_path.should match(/#{cache_dir}/)
|
||||
expect(@cache.storage_path).to match(/#{cache_dir}/)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#serializer' do
|
||||
it 'should return the default serializer : Marshal' do
|
||||
@cache.serializer.should == Marshal
|
||||
@cache.serializer.should_not == YAML
|
||||
expect(@cache.serializer).to eq Marshal
|
||||
expect(@cache.serializer).not_to eq YAML
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,15 +35,31 @@ describe CacheFileStore do
|
||||
File.new(@cache.storage_path + "/file_#{i}.txt", File::CREAT)
|
||||
end
|
||||
|
||||
count_files_in_dir(@cache.storage_path, 'file_*.txt').should == 6
|
||||
expect(count_files_in_dir(@cache.storage_path, 'file_*.txt')).to eq 6
|
||||
@cache.clean
|
||||
count_files_in_dir(@cache.storage_path).should == 0
|
||||
expect(count_files_in_dir(@cache.storage_path)).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
describe '#read_entry (nonexistent entry)' do
|
||||
it 'should return nil' do
|
||||
@cache.read_entry(Digest::SHA1.hexdigest('hello world')).should be_nil
|
||||
describe '#read_entry' do
|
||||
after { expect(@cache.read_entry(key)).to eq @expected }
|
||||
|
||||
context 'when the entry does not exist' do
|
||||
let(:key) { Digest::SHA1.hexdigest('hello world') }
|
||||
|
||||
it 'should return nil' do
|
||||
@expected = nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the file exist but is empty (marshal data too short error)' do
|
||||
let(:key) { 'empty-file' }
|
||||
|
||||
it 'returns nil' do
|
||||
File.new(File.join(@cache.storage_path, key), File::CREAT)
|
||||
|
||||
@expected = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,7 +67,7 @@ describe CacheFileStore do
|
||||
|
||||
after :each do
|
||||
@cache.write_entry(@key, @data, @timeout)
|
||||
@cache.read_entry(@key).should === @expected
|
||||
expect(@cache.read_entry(@key)).to be === @expected
|
||||
end
|
||||
|
||||
it 'should get the correct entry (string)' do
|
||||
@@ -79,7 +95,7 @@ describe CacheFileStore do
|
||||
storage_dirs << CacheFileStore.new(cache_dir).storage_path
|
||||
end
|
||||
|
||||
storage_dirs.uniq.size.should == 5
|
||||
expect(storage_dirs.uniq.size).to eq 5
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,20 +10,15 @@ describe WpItems do
|
||||
|
||||
let(:expected) do
|
||||
{
|
||||
request_params: { cache_ttl: 0, followlocation: true },
|
||||
targets_items_from_file: [ WpItem.new(uri, name: 'item1'),
|
||||
WpItem.new(uri, name:'item-2'),
|
||||
WpItem.new(uri, name: 'mr-smith')],
|
||||
request_params: { cache_ttl: 0, followlocation: true },
|
||||
targets_items_from_file: [ WpItem.new(uri, name: 'item1'),
|
||||
WpItem.new(uri, name: 'item-2'),
|
||||
WpItem.new(uri, name: 'mr-smith')],
|
||||
|
||||
vulnerable_targets_items: [ WpItem.new(uri, name: 'mr-smith'),
|
||||
WpItem.new(uri, name: 'neo')],
|
||||
vulnerable_targets_items: [ WpItem.new(uri, name: 'mr-smith'),
|
||||
WpItem.new(uri, name: 'neo')],
|
||||
|
||||
passive_detection: WpItems.new << WpItem.new(uri, name: 'js-source') <<
|
||||
WpItem.new(uri, name: 'escaped-url') <<
|
||||
WpItem.new(uri, name: 'link-tag') <<
|
||||
WpItem.new(uri, name: 'script-tag') <<
|
||||
WpItem.new(uri, name: 'style-tag') <<
|
||||
WpItem.new(uri, name: 'style-tag-import')
|
||||
passive_detection: (1..13).reduce(WpItems.new) { |o, i| o << WpItem.new(uri, name: "detect-me-#{i}") }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ describe 'WpPlugins::Detectable' do
|
||||
context 'when no header' do
|
||||
it 'returns an empty WpPlugins' do
|
||||
stub_request(:get, url).to_return(status: 200)
|
||||
subject.send(:from_header, wp_target).should == subject.new
|
||||
expect(subject.send(:from_header, wp_target)).to eq subject.new
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,7 @@ describe 'WpPlugins::Detectable' do
|
||||
|
||||
after :each do
|
||||
stub_request(:get, url).to_return(status: 200, headers: headers, body: '')
|
||||
subject.send(:from_header, wp_target).should == expected
|
||||
expect(subject.send(:from_header, wp_target)).to eq expected
|
||||
end
|
||||
|
||||
context 'when w3-total-cache detected' do
|
||||
@@ -66,7 +66,7 @@ describe 'WpPlugins::Detectable' do
|
||||
context 'when no body' do
|
||||
it 'returns an empty WpPlugins' do
|
||||
stub_request(:get, url).to_return(status: 200, body: '')
|
||||
subject.send(:from_content, wp_target).should == subject.new
|
||||
expect(subject.send(:from_content, wp_target)).to eq subject.new
|
||||
end
|
||||
end
|
||||
|
||||
@@ -77,7 +77,7 @@ describe 'WpPlugins::Detectable' do
|
||||
after :each do
|
||||
stub_request(:get, url).to_return(status: 200, body: @body)
|
||||
stub_request(:get, /readme\.txt/i).to_return(status: 404)
|
||||
subject.send(:from_content, wp_target).should == expected
|
||||
expect(subject.send(:from_content, wp_target)).to eq expected
|
||||
end
|
||||
|
||||
context 'when w3 total cache detected' do
|
||||
|
||||
@@ -19,8 +19,7 @@ describe WpPlugins do
|
||||
vulnerable_targets_items: [ WpPlugin.new(uri, name: 'mr-smith'),
|
||||
WpPlugin.new(uri, name: 'neo')],
|
||||
|
||||
passive_detection: WpPlugins.new << WpPlugin.new(uri, name: 'js-source') <<
|
||||
WpPlugin.new(uri, name: 'escaped-url') <<
|
||||
passive_detection: WpPlugins.new << WpPlugin.new(uri, name: 'escaped-url') <<
|
||||
WpPlugin.new(uri, name: 'link-tag') <<
|
||||
WpPlugin.new(uri, name: 'script-tag') <<
|
||||
WpPlugin.new(uri, name: 'style-tag') <<
|
||||
|
||||
@@ -38,7 +38,7 @@ describe 'WpTimthumbs::Detectable' do
|
||||
|
||||
describe '::passive_detection' do
|
||||
it 'returns an empty WpTimthumbs' do
|
||||
subject.passive_detection(wp_target).should == subject.new
|
||||
expect(subject.passive_detection(wp_target)).to eq subject.new
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,7 +46,7 @@ describe 'WpTimthumbs::Detectable' do
|
||||
after do
|
||||
targets = subject.send(:targets_items_from_file, file, wp_target)
|
||||
|
||||
targets.map { |t| t.url }.should == @expected.map { |t| t.url }
|
||||
expect(targets.map { |t| t.url }).to eq @expected.map { |t| t.url }
|
||||
end
|
||||
|
||||
context 'when an empty file' do
|
||||
@@ -71,7 +71,7 @@ describe 'WpTimthumbs::Detectable' do
|
||||
theme = 'hello-world'
|
||||
targets = subject.send(:theme_timthumbs, theme, wp_target)
|
||||
|
||||
targets.map { |t| t.url }.should == expected_targets_from_theme(theme).map { |t| t.url }
|
||||
expect(targets.map { |t| t.url }).to eq expected_targets_from_theme(theme).map { |t| t.url }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -81,7 +81,7 @@ describe 'WpTimthumbs::Detectable' do
|
||||
after do
|
||||
targets = subject.send(:targets_items, wp_target, options)
|
||||
|
||||
targets.map { |t| t.url }.should == @expected.sort.map { |t| t.url }
|
||||
expect(targets.map { |t| t.url }).to eq @expected.sort.map { |t| t.url }
|
||||
end
|
||||
|
||||
context 'when no :theme_name' do
|
||||
|
||||
@@ -22,13 +22,13 @@ describe 'WpUsers::Detectable' do
|
||||
|
||||
describe '::request_params' do
|
||||
it 'return an empty Hash' do
|
||||
subject.request_params.should === {}
|
||||
expect(subject.request_params).to be === {}
|
||||
end
|
||||
end
|
||||
|
||||
describe '::passive_detection' do
|
||||
it 'return an empty WpUsers' do
|
||||
subject.passive_detection(wp_target).should == subject.new
|
||||
expect(subject.passive_detection(wp_target)).to eq subject.new
|
||||
end
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ describe 'WpUsers::Detectable' do
|
||||
after do
|
||||
targets = subject.send(:targets_items, wp_target, options)
|
||||
|
||||
targets.should == @expected
|
||||
expect(targets).to eq @expected
|
||||
end
|
||||
|
||||
context 'when no :range' do
|
||||
|
||||
@@ -25,7 +25,7 @@ describe 'WpUsers::Output' do
|
||||
subject.push(@input)
|
||||
subject.flatten!
|
||||
subject.remove_junk_from_display_names
|
||||
subject.should === @expected
|
||||
expect(subject).to be === @expected
|
||||
end
|
||||
|
||||
it 'should return an empty array' do
|
||||
|
||||
@@ -7,7 +7,7 @@ describe 'common_helper' do
|
||||
after :each do
|
||||
output = get_equal_string_end(@input)
|
||||
|
||||
output.should == @expected
|
||||
expect(output).to eq @expected
|
||||
end
|
||||
|
||||
it 'returns an empty string' do
|
||||
@@ -75,7 +75,7 @@ describe 'common_helper' do
|
||||
describe '#remove_base64_images_from_html' do
|
||||
after :each do
|
||||
output = remove_base64_images_from_html(@html)
|
||||
output.should == @expected
|
||||
expect(output).to eq @expected
|
||||
end
|
||||
|
||||
it 'removes the valid base64 image' do
|
||||
@@ -92,7 +92,7 @@ describe 'common_helper' do
|
||||
describe '#truncate' do
|
||||
after :each do
|
||||
output = truncate(@input, @length, @trailing)
|
||||
output.should == @expected
|
||||
expect(output).to eq @expected
|
||||
end
|
||||
|
||||
it 'returns nil on no input' do
|
||||
|
||||
@@ -15,7 +15,7 @@ describe CustomOptionParser do
|
||||
if @exception
|
||||
expect { CustomOptionParser::option_to_symbol(@option) }.to raise_error(@exception)
|
||||
else
|
||||
CustomOptionParser::option_to_symbol(@option).should === @expected
|
||||
expect(CustomOptionParser::option_to_symbol(@option)).to be === @expected
|
||||
end
|
||||
end
|
||||
|
||||
@@ -100,7 +100,7 @@ describe CustomOptionParser do
|
||||
parser.add_option(['-v', '--verbose'])
|
||||
parser.add_option(['--url TARGET_URL'])
|
||||
|
||||
parser.symbols_used.sort.should === [:url, :verbose]
|
||||
expect(parser.symbols_used.sort).to be === [:url, :verbose]
|
||||
end
|
||||
|
||||
context 'parsing' do
|
||||
@@ -118,7 +118,7 @@ describe CustomOptionParser do
|
||||
|
||||
it 'should retrieve the correct argument' do
|
||||
parser.parse!(['-u', 'iam_the_target'])
|
||||
parser.results.should === { url: 'iam_the_target' }
|
||||
expect(parser.results).to be === { url: 'iam_the_target' }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -134,8 +134,8 @@ describe CustomOptionParser do
|
||||
|
||||
context 'single option' do
|
||||
it 'should add the :url option, and retrieve the correct argument' do
|
||||
parser.symbols_used.should === [:url]
|
||||
parser.results(['-u', 'target.com']).should === { url: 'target.com' }
|
||||
expect(parser.symbols_used).to be === [:url]
|
||||
expect(parser.results(['-u', 'target.com'])).to be === { url: 'target.com' }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -146,8 +146,8 @@ describe CustomOptionParser do
|
||||
['--test [TEST_NUMBER]']
|
||||
])
|
||||
|
||||
parser.symbols_used.sort.should === [:test, :url, :verbose]
|
||||
parser.results(['-u', 'wp.com', '-v', '--test']).should === { test: nil, url: 'wp.com', verbose: true }
|
||||
expect(parser.symbols_used.sort).to be === [:test, :url, :verbose]
|
||||
expect(parser.results(['-u', 'wp.com', '-v', '--test'])).to be === { test: nil, url: 'wp.com', verbose: true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,41 +13,43 @@ describe Vulnerability do
|
||||
context 'w/o metasploit and fixed version modules argument' do
|
||||
subject(:vulnerability) { Vulnerability.new(title, type, references) }
|
||||
|
||||
its(:title) { should be title }
|
||||
its(:references) { should be references }
|
||||
its(:type) { should be type }
|
||||
its(:fixed_in) { should be_empty }
|
||||
its(:title) { should be title }
|
||||
its(:references) { should be references }
|
||||
its(:type) { should be type }
|
||||
its(:fixed_in) { should be_empty }
|
||||
end
|
||||
|
||||
context 'with fixed version argument' do
|
||||
let(:fixed_version) { '1.0' }
|
||||
its(:title) { should be title }
|
||||
its(:references) { should be references }
|
||||
its(:type) { should be type }
|
||||
its(:fixed_in) { should be fixed_version }
|
||||
let(:fixed_version) { '1.0' }
|
||||
|
||||
its(:title) { should be title }
|
||||
its(:references) { should be references }
|
||||
its(:type) { should be type }
|
||||
its(:fixed_in) { should be fixed_version }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '::load_from_xml_node' do
|
||||
subject(:vulnerability) { Vulnerability.load_from_xml_node(node) }
|
||||
let(:node) {
|
||||
xml(MODELS_FIXTURES + '/vulnerability/xml_node.xml').xpath('//vulnerability')
|
||||
describe '::load_from_json_item' do
|
||||
subject(:vulnerability) { Vulnerability.load_from_json_item(item) }
|
||||
let(:item) {
|
||||
json(MODELS_FIXTURES + '/vulnerability/json_item.json')
|
||||
}
|
||||
|
||||
expected_refs = {
|
||||
:url=>['Ref 1', 'Ref 2'],
|
||||
:cve=>['2011-001'],
|
||||
:secunia=>['secunia'],
|
||||
:osvdb=>['osvdb'],
|
||||
:metasploit=>['exploit/ex1'],
|
||||
:exploitdb=>['exploitdb']
|
||||
'id' => ['3911'],
|
||||
'url' => ['Ref 1,Ref 2'],
|
||||
'cve' => ['2011-001'],
|
||||
'secunia' => ['secunia'],
|
||||
'osvdb' => ['osvdb'],
|
||||
'metasploit' => ['exploit/ex1'],
|
||||
'exploitdb' => ['exploitdb']
|
||||
}
|
||||
|
||||
its(:title) { should == 'Vuln Title' }
|
||||
its(:type) { should == 'CSRF' }
|
||||
its(:references) { should == expected_refs}
|
||||
its(:fixed_in) { should == '1.0'}
|
||||
its(:title) { should == 'Vuln Title' }
|
||||
its(:type) { should == 'CSRF' }
|
||||
its(:references) { should == expected_refs}
|
||||
its(:fixed_in) { should == '1.0'}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -11,17 +11,18 @@ describe WpItem do
|
||||
end
|
||||
it_behaves_like 'WpItem::Versionable'
|
||||
it_behaves_like 'WpItem::Vulnerable' do
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_item/vulnerable/items_vulns.xml' }
|
||||
let(:vulns_xpath) { "//item[@name='neo']/vulnerability" }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_item/vulnerable/items_vulns.json' }
|
||||
let(:identifier) { 'neo' }
|
||||
let(:expected_refs) { {
|
||||
:url => ['Ref 1', 'Ref 2'],
|
||||
:cve => ['2011-001'],
|
||||
:secunia => ['secunia'],
|
||||
:osvdb => ['osvdb'],
|
||||
:metasploit => ['exploit/ex1'],
|
||||
:exploitdb => ['exploitdb']
|
||||
'id' => [2993],
|
||||
'url' => ['Ref 1,Ref 2'],
|
||||
'cve' => ['2011-001'],
|
||||
'secunia' => ['secunia'],
|
||||
'osvdb' => ['osvdb'],
|
||||
'metasploit' => ['exploit/ex1'],
|
||||
'exploitdb' => ['exploitdb']
|
||||
} }
|
||||
let(:expected_vulns) { Vulnerabilities.new << Vulnerability.new("I'm the one", 'XSS', expected_refs) }
|
||||
let(:expected_vulns) { Vulnerabilities.new(1, Vulnerability.new("I'm the one", 'XSS', expected_refs)) }
|
||||
end
|
||||
|
||||
subject(:wp_item) { WpItem.new(uri, options) }
|
||||
@@ -30,30 +31,30 @@ describe WpItem do
|
||||
|
||||
describe '#new' do
|
||||
context 'with no options' do
|
||||
its(:wp_content_dir) { should == 'wp-content' }
|
||||
its(:wp_plugins_dir) { should == 'wp-content/plugins' }
|
||||
its(:uri) { should be uri }
|
||||
its(:wp_content_dir) { is_expected.to eq 'wp-content' }
|
||||
its(:wp_plugins_dir) { is_expected.to eq 'wp-content/plugins' }
|
||||
its(:uri) { is_expected.to be uri }
|
||||
end
|
||||
|
||||
context 'with :wp_content_dir' do
|
||||
let(:options) { { wp_content_dir: 'custom' } }
|
||||
|
||||
its(:wp_content_dir) { should == 'custom' }
|
||||
its(:wp_plugins_dir) { should == 'custom/plugins' }
|
||||
its(:wp_content_dir) { is_expected.to eq 'custom' }
|
||||
its(:wp_plugins_dir) { is_expected.to eq 'custom/plugins' }
|
||||
end
|
||||
|
||||
context 'with :wp_plugins_dir' do
|
||||
let(:options) { { wp_plugins_dir: 'c-plugins' } }
|
||||
|
||||
its(:wp_content_dir) { should == 'wp-content' }
|
||||
its(:wp_plugins_dir) { should == 'c-plugins' }
|
||||
its(:wp_content_dir) { is_expected.to eq 'wp-content' }
|
||||
its(:wp_plugins_dir) { is_expected.to eq 'c-plugins' }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#set_options' do
|
||||
context 'no an allowed option' do
|
||||
it 'ignores the option' do
|
||||
wp_item.should_not_receive(:not_allowed=)
|
||||
expect(wp_item).not_to receive(:not_allowed=)
|
||||
|
||||
wp_item.send(:set_options, { not_allowed: 'owned' })
|
||||
end
|
||||
@@ -61,7 +62,7 @@ describe WpItem do
|
||||
|
||||
context 'allowed option, w/o setter method' do
|
||||
it 'raises an error' do
|
||||
wp_item.stub(:allowed_options).and_return([:no_setter])
|
||||
allow(wp_item).to receive(:allowed_options).and_return([:no_setter])
|
||||
|
||||
expect {
|
||||
wp_item.send(:set_options, { no_setter: 'hello' })
|
||||
@@ -73,7 +74,7 @@ describe WpItem do
|
||||
describe '#path=' do
|
||||
after do
|
||||
wp_item.path = @path
|
||||
wp_item.path.should == @expected
|
||||
expect(wp_item.path).to eq @expected
|
||||
end
|
||||
|
||||
context 'with default variable value' do
|
||||
@@ -90,8 +91,8 @@ describe WpItem do
|
||||
|
||||
context 'whith custom variable values' do
|
||||
before {
|
||||
wp_item.stub(:wp_content_dir).and_return('custom-content')
|
||||
wp_item.stub(:wp_plugins_dir).and_return('plugins')
|
||||
allow(wp_item).to receive(:wp_content_dir).and_return('custom-content')
|
||||
allow(wp_item).to receive(:wp_plugins_dir).and_return('plugins')
|
||||
}
|
||||
|
||||
it 'replaces $wp-content$ by custom-content' do
|
||||
@@ -117,7 +118,7 @@ describe WpItem do
|
||||
path = 'somedir/somefile.php'
|
||||
wp_item.path = path
|
||||
|
||||
wp_item.uri.should == uri.merge(path)
|
||||
expect(wp_item.uri).to eq uri.merge(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -127,7 +128,7 @@ describe WpItem do
|
||||
wp_item.name = 'a-name'
|
||||
other = WpItem.new(uri, name: 'other-name')
|
||||
|
||||
wp_item.<=>(other).should === 'a-name'.<=>('other-name')
|
||||
expect(wp_item.<=>(other)).to be === 'a-name'.<=>('other-name')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -137,7 +138,7 @@ describe WpItem do
|
||||
wp_item.name = 'some-name'
|
||||
other = WpItem.new(uri, name: 'some-name')
|
||||
|
||||
wp_item.should == other
|
||||
expect(wp_item).to eq other
|
||||
end
|
||||
end
|
||||
|
||||
@@ -146,7 +147,7 @@ describe WpItem do
|
||||
wp_item.name = 'Test'
|
||||
other = WpItem.new(uri, name: 'hello')
|
||||
|
||||
wp_item.should_not == other
|
||||
expect(wp_item).not_to eq other
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -156,13 +157,13 @@ describe WpItem do
|
||||
|
||||
context 'when the :name and :version are the same' do
|
||||
it 'is ===' do
|
||||
WpItem.new(uri, options).should === WpItem.new(uri.merge('yo'), options)
|
||||
expect(WpItem.new(uri, options)).to be === WpItem.new(uri.merge('yo'), options)
|
||||
end
|
||||
end
|
||||
|
||||
context 'otherwise' do
|
||||
it 'is not ===' do
|
||||
WpItem.new(uri, options).should_not === WpItem.new(uri, options.merge(version: '1.0'))
|
||||
expect(WpItem.new(uri, options)).not_to be === WpItem.new(uri, options.merge(version: '1.0'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,14 +6,15 @@ describe WpPlugin do
|
||||
it_behaves_like 'WpPlugin::Vulnerable'
|
||||
it_behaves_like 'WpItem::Vulnerable' do
|
||||
let(:options) { { name: 'white-rabbit' } }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_plugin/vulnerable/plugins_vulns.xml' }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_plugin/vulnerable/plugins_vulns.json' }
|
||||
let(:expected_refs) { {
|
||||
:url => ['Ref 1', 'Ref 2'],
|
||||
:cve => ['2011-001'],
|
||||
:secunia => ['secunia'],
|
||||
:osvdb => ['osvdb'],
|
||||
:metasploit => ['exploit/ex1'],
|
||||
:exploitdb => ['exploitdb']
|
||||
'id' => [2993],
|
||||
'url' => ['Ref 1,Ref 2'],
|
||||
'cve' => ['2011-001'],
|
||||
'secunia' => ['secunia'],
|
||||
'osvdb' => ['osvdb'],
|
||||
'metasploit' => ['exploit/ex1'],
|
||||
'exploitdb' => ['exploitdb']
|
||||
} }
|
||||
let(:expected_vulns) { Vulnerabilities.new << Vulnerability.new('Follow me!', 'REDIRECT', expected_refs) }
|
||||
end
|
||||
@@ -23,7 +24,7 @@ describe WpPlugin do
|
||||
let(:options) { { name: 'plugin-name' } }
|
||||
|
||||
describe '#forge_uri' do
|
||||
its('uri.to_s') { should == 'http://example.com/wp-content/plugins/plugin-name/' }
|
||||
its('uri.to_s') { is_expected.to eq 'http://example.com/wp-content/plugins/plugin-name/' }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -18,9 +18,9 @@ describe 'WpTheme::Findable' do
|
||||
wp_theme = WpTheme.send(:find_from_css_link, uri)
|
||||
|
||||
if @expected
|
||||
wp_theme.should be_a WpTheme
|
||||
expect(wp_theme).to be_a WpTheme
|
||||
end
|
||||
wp_theme.should == @expected
|
||||
expect(wp_theme).to eq @expected
|
||||
end
|
||||
|
||||
context 'when theme is not present' do
|
||||
@@ -52,6 +52,13 @@ describe 'WpTheme::Findable' do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when other style.css is referenced' do
|
||||
it 'returns the WpTheme' do
|
||||
@file = 'yootheme.html'
|
||||
@expected = WpTheme.new(uri, name: 'yoo_solar_wp', referenced_url: '/wp-content/themes/yoo_solar_wp/styles/wood/css/style.css')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '::find_from_wooframework' do
|
||||
@@ -66,9 +73,9 @@ describe 'WpTheme::Findable' do
|
||||
wp_theme = WpTheme.send(:find_from_wooframework, uri)
|
||||
|
||||
if @expected
|
||||
wp_theme.should be_a WpTheme
|
||||
expect(wp_theme).to be_a WpTheme
|
||||
end
|
||||
wp_theme.should == @expected
|
||||
expect(wp_theme).to eq @expected
|
||||
end
|
||||
|
||||
context 'when theme is not present' do
|
||||
@@ -96,7 +103,7 @@ describe 'WpTheme::Findable' do
|
||||
# Stub all WpTheme::find_from_* to return nil
|
||||
def stub_all_to_nil
|
||||
WpTheme.methods.grep(/^find_from_/).each do |method|
|
||||
WpTheme.stub(method).and_return(nil)
|
||||
allow(WpTheme).to receive(method).and_return(nil)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -120,7 +127,7 @@ describe 'WpTheme::Findable' do
|
||||
it 'returns nil' do
|
||||
stub_all_to_nil()
|
||||
|
||||
WpTheme.find(uri).should be_nil
|
||||
expect(WpTheme.find(uri)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -130,12 +137,12 @@ describe 'WpTheme::Findable' do
|
||||
stub_request(:get, /.+\/the-oracle\/style.css$/).to_return(status: 200)
|
||||
expected = WpTheme.new(uri, name: 'the-oracle')
|
||||
|
||||
WpTheme.stub(:find_from_css_link).and_return(expected)
|
||||
allow(WpTheme).to receive(:find_from_css_link).and_return(expected)
|
||||
wp_theme = WpTheme.find(uri)
|
||||
|
||||
wp_theme.should be_a WpTheme
|
||||
wp_theme.should == expected
|
||||
wp_theme.found_from.should === 'css link'
|
||||
expect(wp_theme).to be_a WpTheme
|
||||
expect(wp_theme).to eq expected
|
||||
expect(wp_theme.found_from).to be === 'css link'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,22 +3,19 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WpTheme do
|
||||
before do
|
||||
stub_request(:get, /.+\/style.css$/).to_return(status: 200)
|
||||
end
|
||||
|
||||
it_behaves_like 'WpTheme::Versionable'
|
||||
it_behaves_like 'WpTheme::Vulnerable'
|
||||
it_behaves_like 'WpItem::Vulnerable' do
|
||||
let(:options) { { name: 'the-oracle' } }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_theme/vulnerable/themes_vulns.xml' }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_theme/vulnerable/themes_vulns.json' }
|
||||
let(:expected_refs) { {
|
||||
:url => ['Ref 1', 'Ref 2'],
|
||||
:cve => ['2011-001'],
|
||||
:secunia => ['secunia'],
|
||||
:osvdb => ['osvdb'],
|
||||
:metasploit => ['exploit/ex1'],
|
||||
:exploitdb => ['exploitdb']
|
||||
'id' => [2993],
|
||||
'url' => ['Ref 1,Ref 2'],
|
||||
'cve' => ['2011-001'],
|
||||
'secunia' => ['secunia'],
|
||||
'osvdb' => ['osvdb'],
|
||||
'metasploit' => ['exploit/ex1'],
|
||||
'exploitdb' => ['exploitdb']
|
||||
} }
|
||||
let(:expected_vulns) { Vulnerabilities.new << Vulnerability.new('I see you', 'FPD', expected_refs) }
|
||||
end
|
||||
@@ -29,24 +26,11 @@ describe WpTheme do
|
||||
let(:theme_path) { 'wp-content/themes/theme-name/' }
|
||||
|
||||
describe '#allowed_options' do
|
||||
its(:allowed_options) { should include :style_url }
|
||||
its(:allowed_options) { is_expected.to include :referenced_url }
|
||||
end
|
||||
|
||||
describe '#forge_uri' do
|
||||
its(:uri) { should == uri.merge(theme_path) }
|
||||
end
|
||||
|
||||
describe '#style_url' do
|
||||
its(:style_url) { should == uri.merge(theme_path + '/style.css').to_s }
|
||||
|
||||
context 'when its already set' do
|
||||
it 'returns it instead of the default one' do
|
||||
url = uri.merge(theme_path + '/custom.css').to_s
|
||||
wp_theme.style_url = url
|
||||
|
||||
wp_theme.style_url.should == url
|
||||
end
|
||||
end
|
||||
its(:uri) { is_expected.to eq uri.merge(theme_path) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -13,17 +13,19 @@ describe WpTimthumb do
|
||||
describe '#==' do
|
||||
context 'when both url are equal' do
|
||||
it 'returns true' do
|
||||
WpTimthumb.new(uri, path: 'timtuhumb.php').
|
||||
should ==
|
||||
expect(WpTimthumb.new(uri, path: 'timtuhumb.php')).
|
||||
to eq(
|
||||
WpTimthumb.new(uri, path: 'timtuhumb.php')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when urls are different' do
|
||||
it 'returns false' do
|
||||
WpTimthumb.new(uri, path: 'hello/timtuhumb.php').
|
||||
should_not ==
|
||||
expect(WpTimthumb.new(uri, path: 'hello/timtuhumb.php')).
|
||||
not_to eq(
|
||||
WpTimthumb.new(uri, path: 'some-dir/timtuhumb.php')
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,10 +12,10 @@ describe WpUser do
|
||||
|
||||
describe '#allowed_options' do
|
||||
[:id, :login, :display_name, :password].each do |sym|
|
||||
its(:allowed_options) { should include sym }
|
||||
its(:allowed_options) { is_expected.to include sym }
|
||||
end
|
||||
|
||||
its(:allowed_options) { should_not include :name }
|
||||
its(:allowed_options) { is_expected.not_to include :name }
|
||||
end
|
||||
|
||||
describe '#uri' do
|
||||
@@ -29,7 +29,7 @@ describe WpUser do
|
||||
it 'returns the uri to the auhor page' do
|
||||
wp_user.id = 2
|
||||
|
||||
wp_user.uri.should == uri.merge('?author=2')
|
||||
expect(wp_user.uri).to eq uri.merge('?author=2')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -37,7 +37,7 @@ describe WpUser do
|
||||
describe '#to_s' do
|
||||
after do
|
||||
subject.id = 1
|
||||
subject.to_s.should == @expected
|
||||
expect(subject.to_s).to eq @expected
|
||||
end
|
||||
|
||||
it 'returns @id' do
|
||||
@@ -67,20 +67,20 @@ describe WpUser do
|
||||
wp_user.id = 1
|
||||
other = WpUser.new(uri, id: 3)
|
||||
|
||||
wp_user.<=>(other).should === 1.<=>(3)
|
||||
expect(wp_user.<=>(other)).to be === 1.<=>(3)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#===, #==' do
|
||||
context 'when the :id and :login are the same' do
|
||||
it 'is ===, and ==' do
|
||||
WpUser.new(uri, id: 1, name: 'yo').should == WpUser.new(uri, id: 1, name: 'yo')
|
||||
expect(WpUser.new(uri, id: 1, name: 'yo')).to eq WpUser.new(uri, id: 1, name: 'yo')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :id and :login are different' do
|
||||
it 'is not === or ==' do
|
||||
WpUser.new(uri, id: 1, name: 'yo').should_not == WpUser.new(uri, id: 2, name:'yo')
|
||||
expect(WpUser.new(uri, id: 1, name: 'yo')).not_to eq WpUser.new(uri, id: 2, name:'yo')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,7 +26,7 @@ describe 'WpVersion::Findable' do
|
||||
fixture = fixtures_dir + dir_name + @fixture
|
||||
stub_request_to_fixture(url: url, fixture: fixture)
|
||||
|
||||
WpVersion.send(method, uri).should == @expected
|
||||
expect(WpVersion.send(method, uri)).to eq @expected
|
||||
end
|
||||
|
||||
context 'when generator not found' do
|
||||
@@ -81,7 +81,7 @@ describe 'WpVersion::Findable' do
|
||||
:find_from_advanced_fingerprinting,
|
||||
uri, wp_content_dir, wp_plugins_dir, versions_xml
|
||||
)
|
||||
version.should == @expected
|
||||
expect(version).to eq @expected
|
||||
end
|
||||
|
||||
context 'when' do
|
||||
@@ -108,7 +108,7 @@ describe 'WpVersion::Findable' do
|
||||
fixture = fixtures_dir + 'readme' + @fixture
|
||||
stub_request_to_fixture(url: url, fixture: fixture)
|
||||
|
||||
WpVersion.send(:find_from_readme, uri).should == @expected
|
||||
expect(WpVersion.send(:find_from_readme, uri)).to eq @expected
|
||||
end
|
||||
|
||||
context 'when version not found' do
|
||||
@@ -138,7 +138,7 @@ describe 'WpVersion::Findable' do
|
||||
fixture = fixtures_dir + 'links_opml' + @fixture
|
||||
stub_request_to_fixture(url: url, fixture: fixture)
|
||||
|
||||
WpVersion.send(:find_from_links_opml, uri).should == @expected
|
||||
expect(WpVersion.send(:find_from_links_opml, uri)).to eq @expected
|
||||
end
|
||||
|
||||
it 'returns 3.4.2' do
|
||||
@@ -158,7 +158,7 @@ describe 'WpVersion::Findable' do
|
||||
# Stub all WpVersion::find_from_* to return nil
|
||||
def stub_all_to_nil
|
||||
WpVersion.methods.grep(/^find_from_/).each do |method|
|
||||
WpVersion.stub(method).and_return(nil)
|
||||
allow(WpVersion).to receive(method).and_return(nil)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -170,9 +170,9 @@ describe 'WpVersion::Findable' do
|
||||
stub_request(:get, /#{uri.to_s}.*/).to_return(status: 0)
|
||||
|
||||
version = WpVersion.find(uri, wp_content_dir, wp_plugins_dir, version_xml)
|
||||
version.should == @expected
|
||||
expect(version).to eq @expected
|
||||
if @expected
|
||||
version.found_from.should == @found_from
|
||||
expect(version.found_from).to eq @found_from
|
||||
end
|
||||
end
|
||||
|
||||
@@ -191,7 +191,7 @@ describe 'WpVersion::Findable' do
|
||||
it "returns the correct WpVersion" do
|
||||
stub_all_to_nil()
|
||||
|
||||
WpVersion.stub(method).and_return(number)
|
||||
allow(WpVersion).to receive(method).and_return(number)
|
||||
|
||||
@expected = WpVersion.new(uri, number: number)
|
||||
@found_from = found_from
|
||||
|
||||
@@ -6,14 +6,15 @@ describe WpVersion do
|
||||
it_behaves_like 'WpVersion::Vulnerable'
|
||||
it_behaves_like 'WpItem::Vulnerable' do
|
||||
let(:options) { { number: '3.2' } }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_version/vulnerable/versions_vulns.xml' }
|
||||
let(:vulns_file) { MODELS_FIXTURES + '/wp_version/vulnerable/versions_vulns.json' }
|
||||
let(:expected_refs) { {
|
||||
:url => ['Ref 1', 'Ref 2'],
|
||||
:cve => ['2011-001'],
|
||||
:secunia => ['secunia'],
|
||||
:osvdb => ['osvdb'],
|
||||
:metasploit => ['exploit/ex1'],
|
||||
:exploitdb => ['exploitdb']
|
||||
'id' => [2993],
|
||||
'url' => ['Ref 1,Ref 2'],
|
||||
'cve' => ['2011-001'],
|
||||
'secunia' => ['secunia'],
|
||||
'osvdb' => ['osvdb'],
|
||||
'metasploit' => ['exploit/ex1'],
|
||||
'exploitdb' => ['exploitdb']
|
||||
} }
|
||||
let(:expected_vulns) { Vulnerabilities.new << Vulnerability.new('Here I Am', 'SQLI', expected_refs) }
|
||||
end
|
||||
@@ -24,7 +25,7 @@ describe WpVersion do
|
||||
|
||||
describe '#allowed_options' do
|
||||
[:number, :found_from].each do |sym|
|
||||
its(:allowed_options) { should include sym }
|
||||
its(:allowed_options) { is_expected.to include sym }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user