Compare commits
730 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21221d48d0 | ||
|
|
1f1a190c84 | ||
|
|
82d79c4662 | ||
|
|
08771a6d5d | ||
|
|
e01d18f224 | ||
|
|
8496650542 | ||
|
|
399245cd0f | ||
|
|
adfa5dddcf | ||
|
|
85971e0e91 | ||
|
|
3a3376ec41 | ||
|
|
d988b6ccbf | ||
|
|
6654f446a4 | ||
|
|
88808db9a5 | ||
|
|
dfad0fd6bd | ||
|
|
3fe49a24c7 | ||
|
|
ac609445fb | ||
|
|
0223f74a53 | ||
|
|
607a5b3fda | ||
|
|
e3ac331a71 | ||
|
|
e09b4cc76d | ||
|
|
c24ed707ef | ||
|
|
a8c55ddee3 | ||
|
|
e080835224 | ||
|
|
2fe675abce | ||
|
|
d230221999 | ||
|
|
91a01265e5 | ||
|
|
77286301a7 | ||
|
|
7c39827c16 | ||
|
|
8f789994eb | ||
|
|
79cb9c8142 | ||
|
|
de1d047c08 | ||
|
|
8252cb486b | ||
|
|
fb8ad72335 | ||
|
|
bc4f0c002b | ||
|
|
0a53c52645 | ||
|
|
7941a8accb | ||
|
|
5389923b34 | ||
|
|
9c1149cb25 | ||
|
|
c5130de805 | ||
|
|
020633503b | ||
|
|
74b9776801 | ||
|
|
5a605d686c | ||
|
|
4ba9bdf605 | ||
|
|
3f647348c3 | ||
|
|
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 | ||
|
|
f30bad5c5a | ||
|
|
5a5bf77b1c | ||
|
|
22c6597005 | ||
|
|
9761d70f23 | ||
|
|
518135006c | ||
|
|
9327eb7e75 | ||
|
|
13080ade9e | ||
|
|
9c3947a7b1 | ||
|
|
f33cbcaa53 | ||
|
|
2c8fe7971f | ||
|
|
85e10330cb | ||
|
|
a02fd4b6f6 | ||
|
|
9395119749 | ||
|
|
2c0bbe9082 | ||
|
|
b1535864aa | ||
|
|
fd880da057 | ||
|
|
6c62b06e46 | ||
|
|
b44869ec49 | ||
|
|
b3a6251b04 | ||
|
|
80667bc38f | ||
|
|
cff35ea845 | ||
|
|
cb93397ae4 | ||
|
|
c805a8d624 | ||
|
|
3f775c081c | ||
|
|
864b892da0 | ||
|
|
f7773d2113 | ||
|
|
cf471d0f36 | ||
|
|
d14791679c | ||
|
|
f358d34e54 | ||
|
|
7ba3a7dd2f | ||
|
|
196af51e2c | ||
|
|
cbe959da66 | ||
|
|
9512fd4cca | ||
|
|
e2b6853c46 | ||
|
|
2578bfd0ff | ||
|
|
277fff5f2c | ||
|
|
4c72e9d206 | ||
|
|
7a44a303e5 | ||
|
|
e3796045eb | ||
|
|
85ad8d8b9f | ||
|
|
f737c92988 | ||
|
|
5d05ecaee3 | ||
|
|
8cc83a8df9 | ||
|
|
43b0f1bea9 | ||
|
|
a12ee4a61e | ||
|
|
d595a45f2b | ||
|
|
76a6d59837 | ||
|
|
bfe9bf2d5b | ||
|
|
59cac169e7 | ||
|
|
c24ee89b16 | ||
|
|
a73c2d7292 | ||
|
|
97671b3592 | ||
|
|
dd38586ead | ||
|
|
e11de68203 | ||
|
|
97ce286ff2 | ||
|
|
99e4e5a219 | ||
|
|
316b9aa68a | ||
|
|
7318d6615f | ||
|
|
6782b108b0 | ||
|
|
494af0ad23 | ||
|
|
55cff03747 | ||
|
|
599cc7c3af | ||
|
|
bfa7a7a6dc | ||
|
|
b243a5404b | ||
|
|
2e40a7377a | ||
|
|
62e86a0474 | ||
|
|
8c1ee9cf21 | ||
|
|
3afda882de | ||
|
|
69fd90ed3a | ||
|
|
554530dd60 | ||
|
|
2472e5546f | ||
|
|
a7ee4c09f5 | ||
|
|
7effac73b9 | ||
|
|
125924d125 | ||
|
|
00aaac866f | ||
|
|
170299c13c | ||
|
|
ac6f56dbf4 | ||
|
|
09c494c6c5 | ||
|
|
fdd685d20c | ||
|
|
fcff22981b | ||
|
|
7f82a41e66 | ||
|
|
17d12b41db | ||
|
|
2f7d68f7f1 | ||
|
|
dc1c65b418 | ||
|
|
23de013c86 | ||
|
|
a922b00716 | ||
|
|
babe21a3dc | ||
|
|
59ef0b05a1 | ||
|
|
3da7ed76c5 | ||
|
|
bbf0cd7578 | ||
|
|
25be97e65c | ||
|
|
175bb9206e | ||
|
|
68605ad409 | ||
|
|
4500687d47 | ||
|
|
fc38e6f270 | ||
|
|
f1936fea6b | ||
|
|
24bdcf66ed | ||
|
|
8688523ced | ||
|
|
856944e463 | ||
|
|
84f6894bc4 | ||
|
|
9bf33f89a9 | ||
|
|
c0fa7c42cd | ||
|
|
3281a85ae9 | ||
|
|
0db965a95f | ||
|
|
0732ea1162 | ||
|
|
c88497afba | ||
|
|
d4892c19b4 | ||
|
|
3ed25b069d | ||
|
|
e51b1f7643 | ||
|
|
7d07b27d4f | ||
|
|
57fdc90623 | ||
|
|
72bec2ef49 | ||
|
|
58a3805788 | ||
|
|
40f96dd2bd | ||
|
|
fca13d453e | ||
|
|
32e590f398 | ||
|
|
301b44dcae | ||
|
|
e45e91b0bf | ||
|
|
fe31dbca26 | ||
|
|
51fa754ec5 | ||
|
|
2ce10af051 | ||
|
|
c107422353 | ||
|
|
1e1fdee5a7 | ||
|
|
fffcd61cc4 | ||
|
|
2fd11cba15 | ||
|
|
f1ec85aef5 | ||
|
|
4c4b363035 | ||
|
|
8555e0c7e7 | ||
|
|
c07f7390ec | ||
|
|
34f6e15271 | ||
|
|
135f0677e4 | ||
|
|
63ad255f2b | ||
|
|
16ac8ea196 | ||
|
|
0e00773238 | ||
|
|
cf1f676268 | ||
|
|
d2f304b047 | ||
|
|
0a9bb138c5 | ||
|
|
6236391c86 | ||
|
|
78257cb1c0 | ||
|
|
c4cb75668c | ||
|
|
9880bcacd6 | ||
|
|
52aedd0a31 | ||
|
|
c7ce741857 | ||
|
|
bcbf012406 | ||
|
|
ec75b4418b | ||
|
|
716b84f507 | ||
|
|
e95b3a59ef | ||
|
|
9dc179b2cd | ||
|
|
efe1b01936 | ||
|
|
cd26b0c5a9 | ||
|
|
b075f71f11 | ||
|
|
44145ef265 | ||
|
|
82ef21b821 | ||
|
|
5655d7456c | ||
|
|
a7992102af | ||
|
|
ee2cd412ce | ||
|
|
1c34f48c39 | ||
|
|
0c2937936b | ||
|
|
b0c3f6c82a | ||
|
|
e047525be9 | ||
|
|
7f1ad590f6 | ||
|
|
fbf2d827c2 | ||
|
|
72619df02c | ||
|
|
c58d8992cf | ||
|
|
c98177ea20 | ||
|
|
766409e581 | ||
|
|
c076d61cc9 | ||
|
|
92e265ecc9 | ||
|
|
e223df64cf | ||
|
|
629ebf5ff2 | ||
|
|
0019eb70b5 | ||
|
|
e5f1be0b30 | ||
|
|
dd74934638 | ||
|
|
8d4973a9cc | ||
|
|
a49c091cd5 | ||
|
|
7d0268a4ce | ||
|
|
44e53e7225 | ||
|
|
1a1689da1f | ||
|
|
a28802e84e | ||
|
|
2798e71a52 | ||
|
|
e3647092b0 | ||
|
|
117cc13ed7 | ||
|
|
2d39a04575 | ||
|
|
d3e9457532 | ||
|
|
afc737b9bd | ||
|
|
32e597f6a4 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@ log.txt
|
|||||||
.yardoc
|
.yardoc
|
||||||
debug.log
|
debug.log
|
||||||
wordlist.txt
|
wordlist.txt
|
||||||
|
rspec_results.html
|
||||||
|
data/
|
||||||
|
|||||||
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.5
|
||||||
20
.travis.yml
20
.travis.yml
@@ -1,6 +1,18 @@
|
|||||||
language: ruby
|
language: ruby
|
||||||
rvm:
|
rvm:
|
||||||
- "1.9.2"
|
- 1.9.2
|
||||||
- "1.9.3"
|
- 1.9.3
|
||||||
- "2.0.0"
|
- 2.0.0
|
||||||
script: bundle exec rspec --format documentation
|
- 2.1.0
|
||||||
|
- 2.1.1
|
||||||
|
- 2.1.2
|
||||||
|
- 2.1.3
|
||||||
|
- 2.1.4
|
||||||
|
- 2.1.5
|
||||||
|
script: bundle exec rspec
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
- wpscanteam@gmail.com
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- rvm: 1.9.2
|
||||||
|
|||||||
257
CHANGELOG.md
257
CHANGELOG.md
@@ -1,9 +1,248 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
## Master
|
||||||
|
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.6...master)
|
||||||
|
|
||||||
## Version 2.2
|
## Version 2.6
|
||||||
|
Released: 2014-12-19
|
||||||
|
|
||||||
|
New
|
||||||
|
* Updates the readmes to reflect the new --usernames option
|
||||||
|
* Improves plugin/theme version detection by looking at the "Version:"
|
||||||
|
* Solution to avoid mandatory blank newline at the end of the wordlist
|
||||||
|
* Add check for valid credentials
|
||||||
|
* Add Sucuri sponsor to banner
|
||||||
|
* Add protocol to sucuri url in banner
|
||||||
|
* Add response code to proxy error output
|
||||||
|
* Add a statement about mendatory newlines at the end of list
|
||||||
|
* Give warning if default username 'admin' is still used
|
||||||
|
* License amendment to make it more clear about value added usage
|
||||||
|
|
||||||
|
Removed
|
||||||
|
* remove malwares
|
||||||
|
* remove malware folder
|
||||||
|
* Removes the theme version check from the readme, unrealistic scenario
|
||||||
|
|
||||||
|
General core
|
||||||
|
* Update to Ruby 2.1.5 and travis
|
||||||
|
* Prevent parent theme infinite loop
|
||||||
|
* Fixes the progressbar being overriden by next brute forcing attempts
|
||||||
|
|
||||||
|
Fixed issues
|
||||||
|
* Fix UTF-8 encode on security db file download
|
||||||
|
* Fix #703 - Disable logging by default. Implement log option.
|
||||||
|
* Fix #705 - Installation instructions for Ubuntu < 14.04 apparently incomplete
|
||||||
|
* Fix #717 - Expand on readme.html finding output
|
||||||
|
* Fix #716 - Adds the --version in the help
|
||||||
|
* Fix #715 - Add new updating info to docs
|
||||||
|
* Fix #727 - WpItems detection: Perform the passive check and filter only vulnerable results at the end if required
|
||||||
|
* Fix #737 - Adds some readme files to check for plugin versions
|
||||||
|
* Fix #739 - Adds the --usernames option
|
||||||
|
|
||||||
|
WPScan Database Statistics:
|
||||||
|
* Total vulnerable versions: 88
|
||||||
|
* Total vulnerable plugins: 901
|
||||||
|
* Total vulnerable themes: 313
|
||||||
|
* Total version vulnerabilities: 1050
|
||||||
|
* Total plugin vulnerabilities: 1355
|
||||||
|
* Total theme vulnerabilities: 349
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
New
|
||||||
|
* Brute forcing over https!
|
||||||
|
* Detect and output parent theme!
|
||||||
|
* Complete fingerprint script & hash search
|
||||||
|
* 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 a "Work in progress" URL in the CHANGELOG
|
||||||
|
|
||||||
|
Removed
|
||||||
|
* Removed "Exiting!" sentence
|
||||||
|
* Removed Backtrack Linux. Not maintained anymore.
|
||||||
|
|
||||||
|
General core
|
||||||
|
* Ruby 2.1.0 added to Travis
|
||||||
|
* Updated the version of WebMock required
|
||||||
|
* Better string concatenation in code (improves speed)
|
||||||
|
* Some modifications in the output of an item
|
||||||
|
* Output cosmetics
|
||||||
|
* rspec-mocks version constraint released
|
||||||
|
* Tabs replaced by spaces
|
||||||
|
* Rspecs update
|
||||||
|
* Indent code cleanup
|
||||||
|
* Themes & Plugins lists regenerated
|
||||||
|
|
||||||
|
Vulnerabilities
|
||||||
|
* Update WordPress Vulnerabilities
|
||||||
|
* Disabled some fake reported vulnerabilities
|
||||||
|
* Fixed some duplicate vulnerabilities
|
||||||
|
|
||||||
|
WPScan Database Statistics:
|
||||||
|
* Total vulnerable versions: 78; 2 are new
|
||||||
|
* Total vulnerable plugins: 693; 83 are new
|
||||||
|
* Total vulnerable themes: 251; 55 are new
|
||||||
|
* Total version vulnerabilities: 291 17 are new
|
||||||
|
* Total plugin vulnerabilities: 1016; 236 are new
|
||||||
|
* Total theme vulnerabilities: 283; 79 are new
|
||||||
|
|
||||||
|
WordPress Fingerprints
|
||||||
|
* Better fingerprints
|
||||||
|
* WP 3.8.1 Fingerprinting
|
||||||
|
* WP 3.8 Fingerprinting
|
||||||
|
|
||||||
|
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 #201 - Incorrect Paramter Parsing when no url was supplied
|
||||||
|
|
||||||
|
## Version 2.2
|
||||||
Released: 2013-11-12
|
Released: 2013-11-12
|
||||||
|
|
||||||
Added
|
New
|
||||||
* Output the vulnerability fix if available
|
* Output the vulnerability fix if available
|
||||||
* Added 'WordPress Version Vulnerability' statistics
|
* Added 'WordPress Version Vulnerability' statistics
|
||||||
* Added Kali Linux on the list of pre-installed Linux distributions
|
* Added Kali Linux on the list of pre-installed Linux distributions
|
||||||
@@ -82,13 +321,13 @@ Vulnerabilities
|
|||||||
* Update timthumb due to Secunia #54801
|
* Update timthumb due to Secunia #54801
|
||||||
* Added WP vuln: 3.4 - 3.5.1 wp-admin/users.php FPD
|
* Added WP vuln: 3.4 - 3.5.1 wp-admin/users.php FPD
|
||||||
|
|
||||||
WPScan Databse Statistics:
|
WPScan Database Statistics:
|
||||||
* Total vulnerable versions: 76, 4 are new
|
* Total vulnerable versions: 76; 4 are new
|
||||||
* Total vulnerable plugins: 606, 197 are new
|
* Total vulnerable plugins: 610; 201 are new
|
||||||
* Total vulnerable themes: 194, 45 are new
|
* Total vulnerable themes: 196; 47 are new
|
||||||
* Total version vulnerabilities: 274, 53 are new
|
* Total version vulnerabilities: 274; 53 are new
|
||||||
* Total plugin vulnerabilities: 764, 270 are new
|
* Total plugin vulnerabilities: 780; 286 are new
|
||||||
* Total theme vulnerabilities: 198, 46 are new
|
* Total theme vulnerabilities: 204; 52 are new
|
||||||
|
|
||||||
Add WP Fingerprints
|
Add WP Fingerprints
|
||||||
* WP 3.7.1 Fingerprinting
|
* WP 3.7.1 Fingerprinting
|
||||||
|
|||||||
9
CREDITS
9
CREDITS
@@ -6,15 +6,16 @@ This file is to give credit to WPScan's contributors. If you feel your name shou
|
|||||||
|
|
||||||
Erwan.LR - @erwan_lr - (Project Developer)
|
Erwan.LR - @erwan_lr - (Project Developer)
|
||||||
Christian Mehlmauer - @_FireFart_ - (Project Developer)
|
Christian Mehlmauer - @_FireFart_ - (Project Developer)
|
||||||
Gianluca Brindisi - @gbrindisi (Project Developer)
|
Peter van der Laan - pvdl - (Vuln Hunter and Code Cleaner)
|
||||||
Ryan Dewhurst - @ethicalhack3r (Project Lead)
|
Ryan Dewhurst - @ethicalhack3r (Project Lead)
|
||||||
|
|
||||||
*Other Contributors*
|
*Other Contributors*
|
||||||
|
|
||||||
|
Henri Salo AKA fgeek - Reported lots of vulnerabilities
|
||||||
Alip AKA Undead - alip.aswalid at gmail.com
|
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
|
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
|
Melvin Lammerts - Reported a couple of fake vulnerabilities - melvin at 12k.nl
|
||||||
Paolo Perego - @thesp0nge - Basic authentication
|
Paolo Perego - @thesp0nge - Basic authentication
|
||||||
Peter van der Laan - The Vuln Hunter and Code Cleaner
|
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.
|
||||||
21
Gemfile
21
Gemfile
@@ -1,15 +1,14 @@
|
|||||||
source "https://rubygems.org"
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
# Seg fault in Typhoeus 0.6.3 (and ethon > 0.5.11) with rspec
|
gem 'typhoeus', '~>0.6.8'
|
||||||
gem "typhoeus", ">=0.6.3"
|
gem 'nokogiri'
|
||||||
gem "nokogiri"
|
gem 'json'
|
||||||
gem "json"
|
gem 'terminal-table'
|
||||||
gem "terminal-table"
|
gem 'ruby-progressbar', '>=1.6.0'
|
||||||
gem "ruby-progressbar", ">=1.2.0"
|
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem "webmock", ">=1.9.3"
|
gem 'webmock', '>=1.17.2'
|
||||||
gem "simplecov"
|
gem 'simplecov'
|
||||||
gem "rspec", :require => "spec"
|
gem 'rspec', '>=3.0'
|
||||||
gem "rspec-mocks", "<=2.14.2" # 2.14.3 just messed around :/
|
gem 'rspec-its'
|
||||||
end
|
end
|
||||||
|
|||||||
30
LICENSE
30
LICENSE
@@ -1,15 +1,21 @@
|
|||||||
WPScan - WordPress Security Scanner
|
The WPScan software and its data (henceforth both referred to simply as "WPScan") is dual-licensed - copyright 2011-2014 The WPScan Team.
|
||||||
Copyright (C) 2012-2013
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
Cases of commercialization are:
|
||||||
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.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
- Using WPScan to provide commercial managed/Software-as-a-Service services.
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
- Distributing WPScan as a commercial product or as part of one.
|
||||||
|
- Using WPScan as a value added service/product.
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
|||||||
149
README
149
README
@@ -9,47 +9,73 @@ __________________________________________________
|
|||||||
|
|
||||||
==LICENSE==
|
==LICENSE==
|
||||||
|
|
||||||
WPScan - WordPress Security Scanner
|
The WPScan software and its data (henceforth both referred to simply as "WPScan") is dual-licensed - copyright 2011-2014 The WPScan Team.
|
||||||
Copyright (C) 2011-2013 The WPScan Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
Cases of commercialization are:
|
||||||
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.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
- Using WPScan to provide commercial managed/Software-as-a-Service services.
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
- Distributing WPScan as a commercial product or as part of one.
|
||||||
|
- Using WPScan as a value added service/product.
|
||||||
|
|
||||||
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==
|
==INSTALL==
|
||||||
|
|
||||||
WPScan comes pre-installed on the following Linux distributions:
|
WPScan comes pre-installed on the following Linux distributions:
|
||||||
|
|
||||||
* BackBox Linux
|
* BackBox Linux
|
||||||
* BackTrack Linux
|
* Kali Linux
|
||||||
* Pentoo
|
* Pentoo
|
||||||
* SamuraiWTF
|
* SamuraiWTF
|
||||||
|
* ArchAssault
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
* Windows not supported
|
* Windows not supported
|
||||||
* Ruby >= 1.9.2 - Recommended: 1.9.3
|
* Ruby >= 1.9.2 - Recommended: 2.1.4
|
||||||
* Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
* Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
||||||
* RubyGems - Recommended: latest
|
* RubyGems - Recommended: latest
|
||||||
* Git
|
* 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
|
If installed from Github update the code base with git pull. The databases are updated with wpscan.rb --update.
|
||||||
|
|
||||||
|
-> Installing on Ubuntu:
|
||||||
|
|
||||||
|
Before Ubuntu 14.04:
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
From Ubuntu 14.04:
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
-> Installing on Debian:
|
||||||
|
|
||||||
|
sudo apt-get install git ruby ruby-dev libcurl4-gnutls-dev make
|
||||||
git clone https://github.com/wpscanteam/wpscan.git
|
git clone https://github.com/wpscanteam/wpscan.git
|
||||||
cd wpscan
|
cd wpscan
|
||||||
sudo gem install bundler && bundle install --without test
|
sudo gem install bundler
|
||||||
|
bundle install --without test --path vendor/bundle
|
||||||
|
|
||||||
-> Installing on Fedora:
|
-> Installing on Fedora:
|
||||||
|
|
||||||
@@ -76,13 +102,26 @@ ryandewhurst at gmail
|
|||||||
|
|
||||||
git clone https://github.com/wpscanteam/wpscan.git
|
git clone https://github.com/wpscanteam/wpscan.git
|
||||||
cd wpscan
|
cd wpscan
|
||||||
sudo gem install bundler && bundle install --without test
|
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.4
|
||||||
|
rvm use 2.1.4 --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==
|
==KNOWN ISSUES==
|
||||||
|
|
||||||
- Typhoeus segmentation fault:
|
- Typhoeus segmentation fault:
|
||||||
Update cURL to version => 7.21 (may have to install from source)
|
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:
|
- Proxy not working:
|
||||||
Update cURL to version => 7.21.7 (may have to install from source).
|
Update cURL to version => 7.21.7 (may have to install from source).
|
||||||
@@ -117,7 +156,7 @@ ryandewhurst at gmail
|
|||||||
|
|
||||||
==WPSCAN ARGUMENTS==
|
==WPSCAN ARGUMENTS==
|
||||||
|
|
||||||
--update Update to the latest revision
|
--update Update the databases.
|
||||||
|
|
||||||
--url | -u <target url> The WordPress URL/domain to scan.
|
--url | -u <target url> The WordPress URL/domain to scan.
|
||||||
|
|
||||||
@@ -132,15 +171,19 @@ ryandewhurst at gmail
|
|||||||
ap all plugins (can take a long time)
|
ap all plugins (can take a long time)
|
||||||
tt timthumbs
|
tt timthumbs
|
||||||
t themes
|
t themes
|
||||||
vp only vulnerable themes
|
vt only vulnerable themes
|
||||||
at all themes (can take a long time)
|
at all themes (can take a long time)
|
||||||
Multiple values are allowed : '-e tt,p' will enumerate timthumbs and plugins
|
Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins
|
||||||
If no option is supplied, the default is 'vt,tt,u,vp'
|
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
|
--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)
|
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
|
--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 +191,39 @@ 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
|
--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).
|
--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
|
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.
|
--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.
|
||||||
|
|
||||||
--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.
|
--username | -U <username> Only brute force the supplied username.
|
||||||
|
|
||||||
|
--usernames <path-to-file> Only brute force the usernames from the file.
|
||||||
|
|
||||||
|
--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.
|
--help | -h This help screen.
|
||||||
|
|
||||||
--verbose | -v Verbose output.
|
--verbose | -v Verbose output.
|
||||||
|
|
||||||
|
--batch Never ask for user input, use the default behaviour.
|
||||||
|
|
||||||
|
--no-color Do not use colors in the output.
|
||||||
|
|
||||||
|
--log Save STDOUT to log.txt
|
||||||
|
|
||||||
==WPSCAN EXAMPLES==
|
==WPSCAN EXAMPLES==
|
||||||
|
|
||||||
Do 'non-intrusive' checks...
|
Do 'non-intrusive' checks...
|
||||||
@@ -191,7 +250,7 @@ Use custom content directory...
|
|||||||
|
|
||||||
ruby wpscan.rb -u www.example.com --wp-content-dir custom-content
|
ruby wpscan.rb -u www.example.com --wp-content-dir custom-content
|
||||||
|
|
||||||
Update WPScan...
|
Update WPScan's databases...
|
||||||
|
|
||||||
ruby wpscan.rb --update
|
ruby wpscan.rb --update
|
||||||
|
|
||||||
@@ -201,19 +260,15 @@ Debug output...
|
|||||||
|
|
||||||
==WPSTOOLS ARGUMENTS==
|
==WPSTOOLS ARGUMENTS==
|
||||||
|
|
||||||
--help | -h This help screen.
|
-v, --verbose Verbose output
|
||||||
--Verbose | -v Verbose output.
|
--check-vuln-ref-urls, --cvru Check all the vulnerabilities reference urls for 404
|
||||||
--update | -u Update to the latest revision.
|
--check-local-vulnerable-files, --clvf LOCAL_DIRECTORY Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells
|
||||||
--generate_plugin_list [number of pages] Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)
|
s, --stats Show WpScan Database statistics.
|
||||||
--gpl Alias for --generate_plugin_list
|
--spellcheck, --sc Check all files for common spelling mistakes.
|
||||||
--check-local-vulnerable-files | --clvf <local directory> Perform a recursive scan in the <local directory> to find vulnerable files or shells
|
|
||||||
|
|
||||||
==WPSTOOLS EXAMPLES==
|
==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/
|
ruby wpstools.rb --check-local-vulnerable-files /var/www/wordpress/
|
||||||
|
|
||||||
===PROJECT HOME===
|
===PROJECT HOME===
|
||||||
@@ -232,8 +287,6 @@ https://github.com/wpscanteam/wpscan/issues
|
|||||||
|
|
||||||
http://rdoc.info/github/wpscanteam/wpscan/frames
|
http://rdoc.info/github/wpscanteam/wpscan/frames
|
||||||
|
|
||||||
===SPONSOR===
|
===SPECIAL THANKS===
|
||||||
|
|
||||||
WPScan is sponsored by the RandomStorm Open Source Initiative.
|
RandomStorm - https://www.randomstorm.com
|
||||||
|
|
||||||
Visit RandomStorm at http://www.randomstorm.com
|
|
||||||
|
|||||||
215
README.md
215
README.md
@@ -1,104 +1,129 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://travis-ci.org/wpscanteam/wpscan)
|
|
||||||
|
[](https://travis-ci.org/wpscanteam/CMSScanner)
|
||||||
|
[](https://codeclimate.com/github/wpscanteam/wpscan)
|
||||||
|
[](https://gemnasium.com/wpscanteam/wpscan)
|
||||||
|
|
||||||
#### LICENSE
|
#### LICENSE
|
||||||
|
|
||||||
WPScan - WordPress Security Scanner
|
The WPScan software and its data (henceforth both referred to simply as "WPScan") is dual-licensed - copyright 2011-2014 The WPScan Team.
|
||||||
Copyright (C), 2011-2013 The WPScan Team
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
Cases of commercialization are:
|
||||||
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.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
- Using WPScan to provide commercial managed/Software-as-a-Service services.
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
- Distributing WPScan as a commercial product or as part of one.
|
||||||
|
- Using WPScan as a value added service/product.
|
||||||
|
|
||||||
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
|
#### INSTALL
|
||||||
|
|
||||||
WPScan comes pre-installed on the following Linux distributions:
|
WPScan comes pre-installed on the following Linux distributions:
|
||||||
|
|
||||||
- [BackBox Linux](http://www.backbox.org/)
|
- [BackBox Linux](http://www.backbox.org/)
|
||||||
- [BackTrack Linux](http://www.backtrack-linux.org/)
|
|
||||||
- [Kali Linux](http://www.kali.org/)
|
- [Kali Linux](http://www.kali.org/)
|
||||||
- [Pentoo](http://www.pentoo.ch/)
|
- [Pentoo](http://www.pentoo.ch/)
|
||||||
- [SamuraiWTF](http://samurai.inguardians.com/)
|
- [SamuraiWTF](http://samurai.inguardians.com/)
|
||||||
|
- [ArchAssault](https://archassault.org/)
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
- Windows not supported
|
- Ruby >= 1.9.2 - Recommended: 2.1.4
|
||||||
- Ruby >= 1.9.2 - Recommended: 1.9.3
|
|
||||||
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
||||||
- RubyGems - Recommended: latest
|
- RubyGems - Recommended: latest
|
||||||
- Git
|
- Git
|
||||||
|
|
||||||
*Installing on Debian/Ubuntu:*
|
Windows is not supported.
|
||||||
|
If installed from Github update the code base with ```git pull```. The databases are updated with ```wpscan.rb --update```.
|
||||||
|
|
||||||
```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
|
||||||
|
git clone https://github.com/wpscanteam/wpscan.git
|
||||||
|
cd wpscan
|
||||||
|
sudo gem install bundler && bundle install --without test
|
||||||
|
|
||||||
```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```
|
cd ~
|
||||||
|
curl -sSL https://get.rvm.io | bash -s stable
|
||||||
```gem install nokogiri```
|
source ~/.rvm/scripts/rvm
|
||||||
|
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
|
||||||
*Installing on Mac OSX:*
|
rvm install 2.1.4
|
||||||
|
rvm use 2.1.4 --default
|
||||||
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
|
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
|
||||||
|
gem install bundler
|
||||||
```git clone https://github.com/wpscanteam/wpscan.git```
|
git clone https://github.com/wpscanteam/wpscan.git
|
||||||
|
cd wpscan
|
||||||
```cd wpscan```
|
bundle install --without test
|
||||||
|
|
||||||
```sudo gem install bundler && bundle install --without test```
|
|
||||||
|
|
||||||
#### KNOWN ISSUES
|
#### KNOWN ISSUES
|
||||||
|
|
||||||
- Typhoeus segmentation fault
|
- Typhoeus segmentation fault
|
||||||
|
|
||||||
Update cURL to version => 7.21 (may have to install from source)
|
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
|
- Proxy not working
|
||||||
|
|
||||||
Update cURL to version => 7.21.7 (may have to install from source).
|
Update cURL to version => 7.21.7 (may have to install from source).
|
||||||
|
|
||||||
Installation from sources :
|
Installation from sources :
|
||||||
```
|
|
||||||
Grab the sources from http://curl.haxx.se/download.html
|
Grab the sources from http://curl.haxx.se/download.html
|
||||||
Decompress the archive
|
Decompress the archive
|
||||||
Open the folder with the extracted files
|
Open the folder with the extracted files
|
||||||
@@ -106,21 +131,21 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
|||||||
Run make
|
Run make
|
||||||
Run sudo make install
|
Run sudo make install
|
||||||
Run sudo ldconfig
|
Run sudo ldconfig
|
||||||
```
|
|
||||||
|
|
||||||
- cannot load such file -- readline:
|
- 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)
|
Then, open the directory of the readline gem (you have to locate it)
|
||||||
```
|
|
||||||
cd ~/.rvm/src/ruby-1.9.2-p180/ext/readline
|
cd ~/.rvm/src/ruby-1.9.2-p180/ext/readline
|
||||||
ruby extconf.rb
|
ruby extconf.rb
|
||||||
make
|
make
|
||||||
make install
|
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
|
- no such file to load -- rubygems
|
||||||
|
|
||||||
@@ -128,11 +153,11 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
|||||||
|
|
||||||
And select your ruby version
|
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
|
#### WPSCAN ARGUMENTS
|
||||||
|
|
||||||
--update Update to the latest revision
|
--update Update the databases.
|
||||||
|
|
||||||
--url | -u <target url> The WordPress URL/domain to scan.
|
--url | -u <target url> The WordPress URL/domain to scan.
|
||||||
|
|
||||||
@@ -149,13 +174,17 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
|||||||
t themes
|
t themes
|
||||||
vt only vulnerable themes
|
vt only vulnerable themes
|
||||||
at all themes (can take a long time)
|
at all themes (can take a long time)
|
||||||
Multiple values are allowed : '-e tt,p' will enumerate timthumbs and plugins
|
Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins
|
||||||
If no option is supplied, the default is 'vt,tt,u,vp'
|
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
|
--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)
|
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
|
--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not
|
||||||
|
|
||||||
@@ -163,23 +192,39 @@ 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
|
--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).
|
--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
|
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.
|
--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.
|
||||||
|
|
||||||
--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.
|
--username | -U <username> Only brute force the supplied username.
|
||||||
|
|
||||||
|
--usernames <path-to-file> Only brute force the usernames from the file.
|
||||||
|
|
||||||
|
--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.
|
--help | -h This help screen.
|
||||||
|
|
||||||
--verbose | -v Verbose output.
|
--verbose | -v Verbose output.
|
||||||
|
|
||||||
|
--batch Never ask for user input, use the default behaviour.
|
||||||
|
|
||||||
|
--no-color Do not use colors in the output.
|
||||||
|
|
||||||
|
--log Save STDOUT to log.txt
|
||||||
|
|
||||||
#### WPSCAN EXAMPLES
|
#### WPSCAN EXAMPLES
|
||||||
|
|
||||||
Do 'non-intrusive' checks...
|
Do 'non-intrusive' checks...
|
||||||
@@ -206,7 +251,7 @@ Use custom content directory...
|
|||||||
|
|
||||||
```ruby wpscan.rb -u www.example.com --wp-content-dir custom-content```
|
```ruby wpscan.rb -u www.example.com --wp-content-dir custom-content```
|
||||||
|
|
||||||
Update WPScan...
|
Update WPScan's databases...
|
||||||
|
|
||||||
```ruby wpscan.rb --update```
|
```ruby wpscan.rb --update```
|
||||||
|
|
||||||
@@ -216,39 +261,39 @@ Debug output...
|
|||||||
|
|
||||||
#### WPSTOOLS ARGUMENTS
|
#### WPSTOOLS ARGUMENTS
|
||||||
|
|
||||||
--help | -h This help screen.
|
-v, --verbose Verbose output
|
||||||
--Verbose | -v Verbose output.
|
--check-vuln-ref-urls, --cvru Check all the vulnerabilities reference urls for 404
|
||||||
--update | -u Update to the latest revision.
|
--check-local-vulnerable-files, --clvf LOCAL_DIRECTORY Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells
|
||||||
--generate_plugin_list [number of pages] Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)
|
-s, --stats Show WpScan Database statistics.
|
||||||
--gpl Alias for --generate_plugin_list
|
--spellcheck, --sc Check all files for common spelling mistakes.
|
||||||
--check-local-vulnerable-files | --clvf <local directory> Perform a recursive scan in the <local directory> to find vulnerable files or shells
|
|
||||||
|
|
||||||
#### WPSTOOLS EXAMPLES
|
#### 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/```
|
```ruby wpstools.rb --check-local-vulnerable-files /var/www/wordpress/```
|
||||||
|
|
||||||
|
|
||||||
#### PROJECT HOME
|
#### 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
|
#### GIT REPOSITORY
|
||||||
|
|
||||||
https://github.com/wpscanteam/wpscan
|
[https://github.com/wpscanteam/wpscan](https://github.com/wpscanteam/wpscan)
|
||||||
|
|
||||||
#### ISSUES
|
#### ISSUES
|
||||||
|
|
||||||
https://github.com/wpscanteam/wpscan/issues
|
[https://github.com/wpscanteam/wpscan/issues](https://github.com/wpscanteam/wpscan/issues)
|
||||||
|
|
||||||
#### DEVELOPER DOCUMENTATION
|
#### DEVELOPER DOCUMENTATION
|
||||||
|
|
||||||
http://rdoc.info/github/wpscanteam/wpscan/frames
|
[http://rdoc.info/github/wpscanteam/wpscan/frames](http://rdoc.info/github/wpscanteam/wpscan/frames)
|
||||||
|
|
||||||
#### SPONSOR
|
#### SPECIAL THANKS
|
||||||
|
|
||||||
WPScan is sponsored by the [RandomStorm](http://www.randomstorm.com) Open Source Initiative.
|
[RandomStorm](https://www.randomstorm.com)
|
||||||
|
|||||||
@@ -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,40 +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:minLength value="1" />
|
|
||||||
</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
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
2205
data/plugins.txt
2205
data/plugins.txt
File diff suppressed because it is too large
Load Diff
38946
data/plugins_full.txt
38946
data/plugins_full.txt
File diff suppressed because it is too large
Load Diff
2067
data/theme_vulns.xml
2067
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 @@
|
|||||||
2013-black-and-white
|
|
||||||
academica
|
|
||||||
accessible-zen
|
|
||||||
adamos
|
|
||||||
adelle
|
|
||||||
admired
|
|
||||||
adventure
|
|
||||||
adventure-journal
|
|
||||||
aldehyde
|
|
||||||
alexandria
|
|
||||||
alhena-lite
|
|
||||||
analytical-lite
|
|
||||||
andrina-lite
|
|
||||||
annotum-base
|
|
||||||
ascetica
|
|
||||||
aspen
|
|
||||||
atahualpa
|
|
||||||
atheros
|
|
||||||
attitude
|
|
||||||
attorney
|
|
||||||
autofocus
|
|
||||||
beach
|
|
||||||
birdsite
|
|
||||||
birdtips
|
|
||||||
biznez-lite
|
|
||||||
bizstudio-lite
|
|
||||||
bizway
|
|
||||||
black-rider
|
|
||||||
blackbird
|
|
||||||
blain
|
|
||||||
blankslate
|
|
||||||
blogbox
|
|
||||||
blogolife
|
|
||||||
bold-headline
|
|
||||||
boldr-lite
|
|
||||||
book-lite
|
|
||||||
boot-store
|
|
||||||
bota
|
|
||||||
bouquet
|
|
||||||
bresponzive
|
|
||||||
bueno
|
|
||||||
business-lite
|
|
||||||
busiprof
|
|
||||||
buzz
|
|
||||||
careta
|
|
||||||
carton
|
|
||||||
catch-box
|
|
||||||
catch-everest
|
|
||||||
catch-evolution
|
|
||||||
cazuela
|
|
||||||
celebrate
|
|
||||||
celestial-lite
|
|
||||||
central
|
|
||||||
chaostheory
|
|
||||||
cherry-blossom
|
|
||||||
childishly-simple
|
|
||||||
church
|
|
||||||
clean-black
|
|
||||||
clean-retina
|
|
||||||
coller
|
|
||||||
colorway
|
|
||||||
contango
|
|
||||||
coraline
|
|
||||||
corpo
|
|
||||||
custom-community
|
|
||||||
customizr
|
|
||||||
cyberchimps
|
|
||||||
cycnus
|
|
||||||
d5-business-line
|
|
||||||
d5-design
|
|
||||||
d5-socialia
|
|
||||||
dailypost
|
|
||||||
decode
|
|
||||||
delicate
|
|
||||||
delighted
|
|
||||||
designfolio
|
|
||||||
destro
|
|
||||||
deux-milles-douze
|
|
||||||
discover
|
|
||||||
dms
|
|
||||||
duena
|
|
||||||
dusk-to-dawn
|
|
||||||
duster
|
|
||||||
dw-minion
|
|
||||||
easel
|
|
||||||
eclipse
|
|
||||||
elegantwhite
|
|
||||||
emphaino
|
|
||||||
encounters-lite
|
|
||||||
engrave-lite
|
|
||||||
epic
|
|
||||||
esplanade
|
|
||||||
espressionista
|
|
||||||
esquire
|
|
||||||
estate
|
|
||||||
evolve
|
|
||||||
expound
|
|
||||||
fashionistas
|
|
||||||
fine
|
|
||||||
firmasite
|
|
||||||
fluxipress
|
|
||||||
focus
|
|
||||||
forever
|
|
||||||
frisco-for-buddypress
|
|
||||||
frontier
|
|
||||||
fruitful
|
|
||||||
futuristica
|
|
||||||
gamepress
|
|
||||||
golden-eagle-lite
|
|
||||||
graphene
|
|
||||||
greenpage
|
|
||||||
gridiculous
|
|
||||||
gridster-lite
|
|
||||||
hannari
|
|
||||||
hatch
|
|
||||||
hazen
|
|
||||||
heatmap-adaptive
|
|
||||||
hero
|
|
||||||
hiero
|
|
||||||
highwind
|
|
||||||
hostmarks
|
|
||||||
houston
|
|
||||||
hro
|
|
||||||
hybrid
|
|
||||||
iconic-one
|
|
||||||
icy
|
|
||||||
ifeature
|
|
||||||
imag-mag
|
|
||||||
impressio-lite
|
|
||||||
impulse
|
|
||||||
infoway
|
|
||||||
innovative
|
|
||||||
intuition
|
|
||||||
irex-lite
|
|
||||||
iribbon
|
|
||||||
kabbo
|
|
||||||
klasik
|
|
||||||
koenda
|
|
||||||
lamya
|
|
||||||
landscape
|
|
||||||
leaf
|
|
||||||
litesta
|
|
||||||
lobster
|
|
||||||
local-business
|
|
||||||
lugada
|
|
||||||
luminescence-lite
|
|
||||||
magazine
|
|
||||||
magazine-basic
|
|
||||||
magazine-style
|
|
||||||
magazino
|
|
||||||
manchester
|
|
||||||
mantra
|
|
||||||
marla
|
|
||||||
max-magazine
|
|
||||||
melany
|
|
||||||
mh-magazine-lite
|
|
||||||
minimatica
|
|
||||||
minimize
|
|
||||||
mixfolio
|
|
||||||
modern-estate
|
|
||||||
mon-cahier
|
|
||||||
montezuma
|
|
||||||
multipurpose
|
|
||||||
my-depressive
|
|
||||||
my-world-with-grass-and-dew
|
|
||||||
mystique
|
|
||||||
narga
|
|
||||||
neuro
|
|
||||||
newp
|
|
||||||
next-saturday
|
|
||||||
omega
|
|
||||||
openstrap
|
|
||||||
opulus-sombre
|
|
||||||
origami
|
|
||||||
origin
|
|
||||||
oxygen
|
|
||||||
p2
|
|
||||||
p2-categories
|
|
||||||
pagelines
|
|
||||||
parabola
|
|
||||||
parament
|
|
||||||
path
|
|
||||||
phonix
|
|
||||||
photographic
|
|
||||||
photolistic
|
|
||||||
photologger
|
|
||||||
pilcrow
|
|
||||||
pilot-fish
|
|
||||||
pinbin
|
|
||||||
pinboard
|
|
||||||
pink-touch-2
|
|
||||||
pitch
|
|
||||||
platform
|
|
||||||
point
|
|
||||||
portfolio-press
|
|
||||||
pr-news
|
|
||||||
prana
|
|
||||||
preference-lite
|
|
||||||
quark
|
|
||||||
r2d2
|
|
||||||
raindrops
|
|
||||||
raptor
|
|
||||||
raven
|
|
||||||
response
|
|
||||||
responsive
|
|
||||||
restaurateur
|
|
||||||
retro
|
|
||||||
road-fighter
|
|
||||||
ryu
|
|
||||||
sampression-lite
|
|
||||||
sensitive
|
|
||||||
serene
|
|
||||||
shprink-one
|
|
||||||
silverclean-lite
|
|
||||||
simple-and-clean
|
|
||||||
simple-catch
|
|
||||||
simpleo
|
|
||||||
simplicity-lite
|
|
||||||
simplify
|
|
||||||
sixteen
|
|
||||||
sliding-door
|
|
||||||
small-business
|
|
||||||
snaps
|
|
||||||
snapshot
|
|
||||||
snowblind
|
|
||||||
socially-awkward
|
|
||||||
spartan
|
|
||||||
spasalon
|
|
||||||
spine
|
|
||||||
spun
|
|
||||||
squirrel
|
|
||||||
startupwp
|
|
||||||
steira
|
|
||||||
strapvert
|
|
||||||
striker
|
|
||||||
suevafree
|
|
||||||
suffusion
|
|
||||||
suits
|
|
||||||
sukelius-magazine
|
|
||||||
sundance
|
|
||||||
sunny-blue-sky
|
|
||||||
sunspot
|
|
||||||
supernova
|
|
||||||
surfarama
|
|
||||||
sweet-tech
|
|
||||||
swift-basic
|
|
||||||
tampa
|
|
||||||
target
|
|
||||||
teal
|
|
||||||
terrifico
|
|
||||||
tesla
|
|
||||||
the-bootstrap
|
|
||||||
thematic
|
|
||||||
themia-lite
|
|
||||||
theron-lite
|
|
||||||
timeturner
|
|
||||||
tiny-forge
|
|
||||||
tonic
|
|
||||||
toolbox
|
|
||||||
travel-blogger
|
|
||||||
travelify
|
|
||||||
tribbiani
|
|
||||||
twentyeleven
|
|
||||||
twentyten
|
|
||||||
twentythirteen
|
|
||||||
twentytwelve
|
|
||||||
unique
|
|
||||||
untitled
|
|
||||||
uptown
|
|
||||||
vantage
|
|
||||||
viper
|
|
||||||
virtue
|
|
||||||
visitpress
|
|
||||||
visual
|
|
||||||
vortex
|
|
||||||
voyage
|
|
||||||
ward
|
|
||||||
weaver-ii
|
|
||||||
wordpost
|
|
||||||
wp-creativix
|
|
||||||
wp-flatthirteen
|
|
||||||
wp-knowledge-base
|
|
||||||
wp-opulus
|
|
||||||
xin-magazine
|
|
||||||
yoko
|
|
||||||
zalive
|
|
||||||
zbench
|
|
||||||
zeebizzcard
|
|
||||||
zeebusiness
|
|
||||||
zeedynamic
|
|
||||||
zeeflow
|
|
||||||
zeefocus
|
|
||||||
zeemagazine
|
|
||||||
zeeminty
|
|
||||||
zeenews
|
|
||||||
zeenoble
|
|
||||||
zeestyle
|
|
||||||
zeesynergie
|
|
||||||
zeetasty
|
|
||||||
zenon-lite
|
|
||||||
7549
data/themes_full.txt
7549
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
106
data/vuln.xsd
106
data/vuln.xsd
@@ -1,106 +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:minLength value="1" />
|
|
||||||
</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,223 +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/js/tinymce/plugins/wpeditimage/editor_plugin_src.js">
|
|
||||||
<hash md5="5d01c0e812cdcd6356b78ee0cb4e5426">
|
|
||||||
<version>3.7.1</version>
|
|
||||||
</hash>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file src="wp-includes/js/jquery/jquery.form.js">
|
|
||||||
<hash md5="e5afd8e41d2ec22c19932b068cd90a71">
|
|
||||||
<version>3.7</version>
|
|
||||||
</hash>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file src="wp-admin/js/common.js">
|
|
||||||
<hash md5="03eaffeef39119f0523a49c7f9767f3b">
|
|
||||||
<version>3.6.1</version>
|
|
||||||
</hash>
|
|
||||||
<hash md5="4516252d47a73630280869994d510180">
|
|
||||||
<version>3.3</version>
|
|
||||||
</hash>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file src="wp-includes/js/jquery/jquery.js">
|
|
||||||
<hash md5="9dcde2d5e8aeda556a0c52239fa2f44c">
|
|
||||||
<version>3.6</version>
|
|
||||||
</hash>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file src="wp-includes/js/tinymce/tiny_mce.js">
|
|
||||||
<hash md5="eddb5fda74d41dbdac018167536d8d53">
|
|
||||||
<version>3.5.2</version>
|
|
||||||
</hash>
|
|
||||||
|
|
||||||
<hash md5="6e79ab6d786c5c95920064add33ee599">
|
|
||||||
<version>3.5.1</version>
|
|
||||||
</hash>
|
|
||||||
|
|
||||||
<hash md5="55cd8e5ceca9c1763b1401164d70df50">
|
|
||||||
<version>3.5</version>
|
|
||||||
</hash>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file src="wp-includes/js/wp-lists.js">
|
|
||||||
<hash md5="46e1341cd4ea49f31046f7d7962adc7f">
|
|
||||||
<version>3.4.2</version>
|
|
||||||
</hash>
|
|
||||||
</file>
|
|
||||||
|
|
||||||
<file src="wp-includes/js/customize-preview.js">
|
|
||||||
<hash md5="617d9fd858e117c7d1d087be168b5643">
|
|
||||||
<version>3.4.1</version>
|
|
||||||
</hash>
|
|
||||||
|
|
||||||
<hash md5="da36bc2dfcb13350c799b62de68dfa4b">
|
|
||||||
<version>3.4</version>
|
|
||||||
</hash>
|
|
||||||
|
|
||||||
<hash md5="a8a259fc5197a78ffe62d6be38dc52f8">
|
|
||||||
<version>3.4-beta4</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/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,39 +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:minLength value="1" />
|
|
||||||
</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>
|
|
||||||
2230
data/wp_vulns.xml
2230
data/wp_vulns.xml
File diff suppressed because it is too large
Load Diff
41
dev/pre-commit-hook.rb
Executable file
41
dev/pre-commit-hook.rb
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# from the top level dir:
|
||||||
|
# ln -sf ../../dev/pre-commit-hook.rb .git/hooks/pre-commit
|
||||||
|
|
||||||
|
require 'pty'
|
||||||
|
html_path = 'rspec_results.html'
|
||||||
|
|
||||||
|
begin
|
||||||
|
PTY.spawn( "rspec spec --format h > #{html_path}" ) do |stdin, stdout, pid|
|
||||||
|
begin
|
||||||
|
stdin.each { |line| print line }
|
||||||
|
rescue Errno::EIO => e
|
||||||
|
puts "Error: #{e.to.s}"
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue PTY::ChildExited
|
||||||
|
puts 'Child process exit!'
|
||||||
|
end
|
||||||
|
|
||||||
|
# find out if there were any errors
|
||||||
|
html = open(html_path).read
|
||||||
|
examples = html.match(/(\d+) examples/)[0].to_i rescue 0
|
||||||
|
errors = html.match(/(\d+) errors/)[0].to_i rescue 0
|
||||||
|
if errors == 0 then
|
||||||
|
errors = html.match(/(\d+) failure/)[0].to_i rescue 0
|
||||||
|
end
|
||||||
|
pending = html.match(/(\d+) pending/)[0].to_i rescue 0
|
||||||
|
|
||||||
|
if errors.zero?
|
||||||
|
puts "0 failed! #{examples} run, #{pending} pending"
|
||||||
|
sleep 1
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
puts "\aCOMMIT FAILED!!"
|
||||||
|
puts "View your rspec results at #{File.expand_path(html_path)}"
|
||||||
|
puts
|
||||||
|
puts "#{errors} failed! #{examples} run, #{pending} pending"
|
||||||
|
exit 1
|
||||||
|
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
|
include Browser::Options
|
||||||
|
|
||||||
OPTIONS = [
|
OPTIONS = [
|
||||||
:available_user_agents,
|
|
||||||
:basic_auth,
|
:basic_auth,
|
||||||
:cache_ttl,
|
:cache_ttl,
|
||||||
:max_threads,
|
:max_threads,
|
||||||
:user_agent,
|
:user_agent,
|
||||||
:user_agent_mode,
|
|
||||||
:proxy,
|
:proxy,
|
||||||
:proxy_auth,
|
:proxy_auth,
|
||||||
:request_timeout,
|
:request_timeout,
|
||||||
:connect_timeout
|
:connect_timeout,
|
||||||
|
:cookie
|
||||||
]
|
]
|
||||||
|
|
||||||
@@instance = nil
|
@@instance = nil
|
||||||
|
|
||||||
attr_reader :hydra, :config_file, :cache_dir
|
attr_reader :hydra, :cache_dir
|
||||||
|
|
||||||
|
attr_accessor :referer, :cookie
|
||||||
|
|
||||||
# @param [ Hash ] options
|
# @param [ Hash ] options
|
||||||
#
|
#
|
||||||
# @return [ Browser ]
|
# @return [ Browser ]
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
@config_file = options[:config_file] || CONF_DIR + '/browser.conf.json'
|
@cache_dir = options[:cache_dir] || CACHE_DIR + '/browser'
|
||||||
@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)
|
override_config(options)
|
||||||
|
|
||||||
unless @hydra
|
unless @hydra
|
||||||
@@ -61,6 +66,20 @@ class Browser
|
|||||||
@@instance = nil
|
@@instance = nil
|
||||||
end
|
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
|
# If an option was set but is not in the new config_file
|
||||||
# it's value is kept
|
# it's value is kept
|
||||||
@@ -69,21 +88,20 @@ class Browser
|
|||||||
#
|
#
|
||||||
# @return [ void ]
|
# @return [ void ]
|
||||||
def load_config(config_file = nil)
|
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.'
|
raise '[ERROR] Config file is a symlink.'
|
||||||
else
|
else
|
||||||
data = JSON.parse(File.read(@config_file))
|
data = JSON.parse(File.read(config_file))
|
||||||
end
|
end
|
||||||
|
|
||||||
OPTIONS.each do |option|
|
OPTIONS.each do |option|
|
||||||
option_name = option.to_s
|
option_name = option.to_s
|
||||||
|
|
||||||
unless data[option_name].nil?
|
unless data[option_name].nil?
|
||||||
self.send(:"#{option_name}=", data[option_name])
|
self.send(:"#{option_name}=", data[option_name])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [ String ] url
|
# @param [ String ] url
|
||||||
@@ -101,7 +119,7 @@ class Browser
|
|||||||
params = Browser.append_params_header_field(
|
params = Browser.append_params_header_field(
|
||||||
params,
|
params,
|
||||||
'User-Agent',
|
'User-Agent',
|
||||||
self.user_agent
|
@user_agent
|
||||||
)
|
)
|
||||||
|
|
||||||
if @proxy
|
if @proxy
|
||||||
@@ -120,18 +138,15 @@ class Browser
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
if @request_timeout
|
params.merge!(referer: referer)
|
||||||
params = params.merge(timeout: @request_timeout)
|
params.merge!(timeout: @request_timeout) if @request_timeout
|
||||||
end
|
params.merge!(connecttimeout: @connect_timeout) if @connect_timeout
|
||||||
|
|
||||||
if @connect_timeout
|
|
||||||
params = params.merge(connecttimeout: @connect_timeout)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Used to enable the cache system if :cache_ttl > 0
|
# Used to enable the cache system if :cache_ttl > 0
|
||||||
unless params.has_key?(:cache_ttl)
|
params.merge!(cache_ttl: @cache_ttl) unless params.has_key?(:cache_ttl)
|
||||||
params = params.merge(cache_ttl: @cache_ttl)
|
|
||||||
end
|
# Prevent infinite self redirection
|
||||||
|
params.merge!(maxredirs: 3) unless params.has_key?(:maxredirs)
|
||||||
|
|
||||||
# Disable SSL-Certificate checks
|
# Disable SSL-Certificate checks
|
||||||
params.merge!(ssl_verifypeer: false)
|
params.merge!(ssl_verifypeer: false)
|
||||||
@@ -139,6 +154,7 @@ class Browser
|
|||||||
|
|
||||||
params.merge!(cookiejar: @cache_dir + '/cookie-jar')
|
params.merge!(cookiejar: @cache_dir + '/cookie-jar')
|
||||||
params.merge!(cookiefile: @cache_dir + '/cookie-jar')
|
params.merge!(cookiefile: @cache_dir + '/cookie-jar')
|
||||||
|
params.merge!(cookie: @cookie) if @cookie
|
||||||
|
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
class Browser
|
class Browser
|
||||||
module Options
|
module Options
|
||||||
|
|
||||||
USER_AGENT_MODES = %w{ static semi-static random }
|
attr_accessor :cache_ttl, :request_timeout, :connect_timeout
|
||||||
|
attr_reader :basic_auth, :proxy, :proxy_auth
|
||||||
attr_accessor :available_user_agents, :cache_ttl, :request_timeout, :connect_timeout
|
|
||||||
attr_reader :basic_auth, :user_agent_mode, :proxy, :proxy_auth
|
|
||||||
attr_writer :user_agent
|
attr_writer :user_agent
|
||||||
|
|
||||||
# Sets the Basic Authentification credentials
|
# Sets the Basic Authentification credentials
|
||||||
@@ -41,42 +39,6 @@ class Browser
|
|||||||
end
|
end
|
||||||
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
|
# Sets the proxy
|
||||||
# Accepted format:
|
# Accepted format:
|
||||||
# [protocol://]host:post
|
# [protocol://]host:post
|
||||||
|
|||||||
@@ -9,17 +9,19 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
class CacheFileStore
|
class CacheFileStore
|
||||||
attr_reader :storage_path, :serializer
|
attr_reader :storage_path, :cache_dir, :serializer
|
||||||
|
|
||||||
# The serializer must have the 2 methods .load and .dump
|
# The serializer must have the 2 methods .load and .dump
|
||||||
# (Marshal and YAML have them)
|
# (Marshal and YAML have them)
|
||||||
# YAML is Human Readable, contrary to Marshal which store in a binary format
|
# YAML is Human Readable, contrary to Marshal which store in a binary format
|
||||||
# Marshal does not need any "require"
|
# Marshal does not need any "require"
|
||||||
def initialize(storage_path, serializer = Marshal)
|
def initialize(storage_path, serializer = Marshal)
|
||||||
@storage_path = File.expand_path(storage_path)
|
@cache_dir = File.expand_path(storage_path)
|
||||||
@serializer = serializer
|
@storage_path = File.expand_path(File.join(storage_path, storage_dir))
|
||||||
|
@serializer = serializer
|
||||||
|
|
||||||
# File.directory? for ruby <= 1.9 otherwise,
|
# File.directory? for ruby <= 1.9 otherwise,
|
||||||
# it makes more sense to do Dir.exist? :/
|
# it makes more sense to do Dir.exist? :/
|
||||||
@@ -29,16 +31,22 @@ class CacheFileStore
|
|||||||
end
|
end
|
||||||
|
|
||||||
def clean
|
def clean
|
||||||
Dir[File.join(@storage_path, '*')].each do |f|
|
# clean old directories
|
||||||
File.delete(f) unless File.symlink?(f)
|
Dir[File.join(@cache_dir, '*')].each do |f|
|
||||||
|
if File.directory?(f)
|
||||||
|
# delete directory if create time is older than 4 hours
|
||||||
|
FileUtils.rm_rf(f) if File.mtime(f) < (Time.now - (60*240))
|
||||||
|
else
|
||||||
|
File.delete(f) unless File.symlink?(f)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_entry(key)
|
def read_entry(key)
|
||||||
entry_file_path = get_entry_file_path(key)
|
begin
|
||||||
|
@serializer.load(File.read(get_entry_file_path(key)))
|
||||||
if File.exists?(entry_file_path)
|
rescue
|
||||||
return @serializer.load(File.read(entry_file_path))
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -58,4 +66,11 @@ class CacheFileStore
|
|||||||
File::join(@storage_path, key)
|
File::join(@storage_path, key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def storage_dir
|
||||||
|
time = Time.now
|
||||||
|
random = (0...8).map { (65 + rand(26)).chr }.join
|
||||||
|
|
||||||
|
Digest::MD5.hexdigest("#{time}#{random}")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
class Vulnerabilities < Array
|
class Vulnerabilities < Array
|
||||||
module Output
|
module Output
|
||||||
|
|
||||||
def output
|
def output(verbose = false)
|
||||||
self.each do |v|
|
self.each do |v|
|
||||||
v.output
|
v.output(verbose)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class WpItems < Array
|
|||||||
self.wp_target = wp_target
|
self.wp_target = wp_target
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [String,] argv
|
# @param [String] argv
|
||||||
#
|
#
|
||||||
# @return [ void ]
|
# @return [ void ]
|
||||||
def add(*args)
|
def add(*args)
|
||||||
|
|||||||
@@ -17,15 +17,13 @@ class WpItems < Array
|
|||||||
hydra = browser.hydra
|
hydra = browser.hydra
|
||||||
targets = targets_items(wp_target, options)
|
targets = targets_items(wp_target, options)
|
||||||
progress_bar = progress_bar(targets.size, options)
|
progress_bar = progress_bar(targets.size, options)
|
||||||
|
queue_count = 0
|
||||||
exist_options = {
|
exist_options = {
|
||||||
error_404_hash: wp_target.error_404_hash,
|
error_404_hash: wp_target.error_404_hash,
|
||||||
homepage_hash: wp_target.homepage_hash,
|
homepage_hash: wp_target.homepage_hash,
|
||||||
exclude_content: options[:exclude_content] ? %r{#{options[:exclude_content]}} : nil
|
exclude_content: options[:exclude_content] ? %r{#{options[:exclude_content]}} : nil
|
||||||
}
|
}
|
||||||
|
results = passive_detection(wp_target, options)
|
||||||
# If we only want the vulnerable ones, the passive detection is ignored
|
|
||||||
# Otherwise, a passive detection is performed, and results will be merged
|
|
||||||
results = options[:only_vulnerable] ? new : passive_detection(wp_target, options)
|
|
||||||
|
|
||||||
targets.each do |target_item|
|
targets.each do |target_item|
|
||||||
request = browser.forge_request(target_item.url, request_params)
|
request = browser.forge_request(target_item.url, request_params)
|
||||||
@@ -43,11 +41,22 @@ class WpItems < Array
|
|||||||
end
|
end
|
||||||
|
|
||||||
hydra.queue(request)
|
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
|
end
|
||||||
|
|
||||||
|
# run the remaining requests
|
||||||
hydra.run
|
hydra.run
|
||||||
|
|
||||||
|
results.select!(&:vulnerable?) if options[:only_vulnerable]
|
||||||
results.sort!
|
results.sort!
|
||||||
results # can't just return results.sort because the #sort returns an array, and we want a WpItems
|
|
||||||
|
results # can't just return results.sort as it would return an array, and we want a WpItems
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [ Integer ] targets_size
|
# @param [ Integer ] targets_size
|
||||||
@@ -71,13 +80,31 @@ class WpItems < Array
|
|||||||
#
|
#
|
||||||
# @return [ WpItems ]
|
# @return [ WpItems ]
|
||||||
def passive_detection(wp_target, options = {})
|
def passive_detection(wp_target, options = {})
|
||||||
results = new(wp_target)
|
results = new(wp_target)
|
||||||
body = Browser.get(wp_target.url).body
|
|
||||||
# improves speed
|
# improves speed
|
||||||
body = remove_base64_images_from_html(body)
|
body = remove_base64_images_from_html(Browser.get(wp_target.url).body)
|
||||||
names = body.scan(passive_detection_pattern(wp_target))
|
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.sort!
|
||||||
results
|
results
|
||||||
@@ -88,13 +115,29 @@ class WpItems < Array
|
|||||||
# @param [ WpTarget ] wp_target
|
# @param [ WpTarget ] wp_target
|
||||||
#
|
#
|
||||||
# @return [ Regex ]
|
# @return [ Regex ]
|
||||||
def passive_detection_pattern(wp_target)
|
def item_pattern(wp_target)
|
||||||
type = self.to_s.gsub(/Wp/, '').downcase
|
type = to_s.gsub(/Wp/, '').downcase
|
||||||
regex1 = %r{(?:[^=:\(]+)\s?(?:=|:|\()\s?(?:"|')[^"']+\\?/}
|
wp_content_dir = wp_target.wp_content_dir
|
||||||
regex2 = %r{\\?/}
|
wp_content_url = wp_target.uri.merge(wp_content_dir).to_s
|
||||||
regex3 = %r{\\?/([^/\\"']+)\\?(?:/|"|')}
|
|
||||||
|
|
||||||
/#{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
|
end
|
||||||
|
|
||||||
# The default request parameters
|
# The default request parameters
|
||||||
@@ -133,16 +176,17 @@ class WpItems < Array
|
|||||||
# @return [ Array<WpItem> ]
|
# @return [ Array<WpItem> ]
|
||||||
def vulnerable_targets_items(wp_target, item_class, vulns_file)
|
def vulnerable_targets_items(wp_target, item_class, vulns_file)
|
||||||
targets = []
|
targets = []
|
||||||
xml = xml(vulns_file)
|
json = json(vulns_file)
|
||||||
|
|
||||||
xml.xpath(item_xpath).each do |node|
|
[*json].each do |item|
|
||||||
targets << create_item(
|
targets << create_item(
|
||||||
item_class,
|
item_class,
|
||||||
node.attribute('name').text,
|
item.keys.inject,
|
||||||
wp_target,
|
wp_target,
|
||||||
vulns_file
|
vulns_file
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
targets
|
targets
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -181,6 +225,7 @@ class WpItems < Array
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
targets
|
targets
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
class WpItems < Array
|
class WpItems < Array
|
||||||
module Output
|
module Output
|
||||||
|
|
||||||
def output
|
def output(verbose = false)
|
||||||
self.each { |item| item.output }
|
self.each { |item| item.output(verbose) }
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ class WpPlugins < WpItems
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def item_xpath
|
# def item_xpath
|
||||||
'//plugin'
|
# '//plugin'
|
||||||
end
|
# end
|
||||||
|
|
||||||
# @param [ WpTarget ] wp_target
|
# @param [ WpTarget ] wp_target
|
||||||
# @param [ Hash ] options
|
# @param [ Hash ] options
|
||||||
@@ -68,6 +68,10 @@ class WpPlugins < WpItems
|
|||||||
wp_plugins.add('all-in-one-seo-pack', version: $1)
|
wp_plugins.add('all-in-one-seo-pack', version: $1)
|
||||||
end
|
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
|
wp_plugins
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ class WpThemes < WpItems
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def item_xpath
|
# def item_xpath
|
||||||
'//theme'
|
# '//theme'
|
||||||
end
|
# end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class WpUsers < WpItems
|
|||||||
junk = get_equal_string_end(display_names)
|
junk = get_equal_string_end(display_names)
|
||||||
unless junk.nil? or junk.empty?
|
unless junk.nil? or junk.empty?
|
||||||
self.each do |u|
|
self.each do |u|
|
||||||
|
u.display_name ||= ''
|
||||||
u.display_name = u.display_name.sub(/#{Regexp.escape(junk)}$/, '')
|
u.display_name = u.display_name.sub(/#{Regexp.escape(junk)}$/, '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,39 +1,40 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
LIB_DIR = File.expand_path(File.dirname(__FILE__) + '/..')
|
LIB_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
||||||
ROOT_DIR = File.expand_path(LIB_DIR + '/..') # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
ROOT_DIR = File.expand_path(File.join(LIB_DIR, '..')) # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
||||||
DATA_DIR = ROOT_DIR + '/data'
|
DATA_DIR = File.join(ROOT_DIR, 'data')
|
||||||
CONF_DIR = ROOT_DIR + '/conf'
|
CONF_DIR = File.join(ROOT_DIR, 'conf')
|
||||||
CACHE_DIR = ROOT_DIR + '/cache'
|
CACHE_DIR = File.join(ROOT_DIR, 'cache')
|
||||||
WPSCAN_LIB_DIR = LIB_DIR + '/wpscan'
|
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan')
|
||||||
WPSTOOLS_LIB_DIR = LIB_DIR + '/wpstools'
|
WPSTOOLS_LIB_DIR = File.join(LIB_DIR, 'wpstools')
|
||||||
UPDATER_LIB_DIR = LIB_DIR + '/updater'
|
UPDATER_LIB_DIR = File.join(LIB_DIR, 'updater')
|
||||||
COMMON_LIB_DIR = LIB_DIR + '/common'
|
COMMON_LIB_DIR = File.join(LIB_DIR, 'common')
|
||||||
MODELS_LIB_DIR = COMMON_LIB_DIR + '/models'
|
MODELS_LIB_DIR = File.join(COMMON_LIB_DIR, 'models')
|
||||||
COLLECTIONS_LIB_DIR = COMMON_LIB_DIR + '/collections'
|
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
|
# Plugins directories
|
||||||
COMMON_PLUGINS_DIR = COMMON_LIB_DIR + '/plugins'
|
COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
|
||||||
WPSCAN_PLUGINS_DIR = WPSCAN_LIB_DIR + '/plugins' # Not used ATM
|
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
|
||||||
WPSTOOLS_PLUGINS_DIR = WPSTOOLS_LIB_DIR + '/plugins'
|
WPSTOOLS_PLUGINS_DIR = File.join(WPSTOOLS_LIB_DIR, 'plugins')
|
||||||
|
|
||||||
# Data files
|
# Data files
|
||||||
PLUGINS_FILE = DATA_DIR + '/plugins.txt'
|
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.txt')
|
||||||
PLUGINS_FULL_FILE = DATA_DIR + '/plugins_full.txt'
|
PLUGINS_FULL_FILE = File.join(DATA_DIR, 'plugins_full.txt')
|
||||||
PLUGINS_VULNS_FILE = DATA_DIR + '/plugin_vulns.xml'
|
PLUGINS_VULNS_FILE = File.join(DATA_DIR, 'plugin_vulns.json')
|
||||||
THEMES_FILE = DATA_DIR + '/themes.txt'
|
THEMES_FILE = File.join(DATA_DIR, 'themes.txt')
|
||||||
THEMES_FULL_FILE = DATA_DIR + '/themes_full.txt'
|
THEMES_FULL_FILE = File.join(DATA_DIR, 'themes_full.txt')
|
||||||
THEMES_VULNS_FILE = DATA_DIR + '/theme_vulns.xml'
|
THEMES_VULNS_FILE = File.join(DATA_DIR, 'theme_vulns.json')
|
||||||
WP_VULNS_FILE = DATA_DIR + '/wp_vulns.xml'
|
WP_VULNS_FILE = File.join(DATA_DIR, 'wp_vulns.json')
|
||||||
WP_VERSIONS_FILE = DATA_DIR + '/wp_versions.xml'
|
WP_VERSIONS_FILE = File.join(DATA_DIR, 'wp_versions.xml')
|
||||||
LOCAL_FILES_FILE = DATA_DIR + '/local_vulnerable_files.xml'
|
LOCAL_FILES_FILE = File.join(DATA_DIR, 'local_vulnerable_files.xml')
|
||||||
VULNS_XSD = DATA_DIR + '/vuln.xsd'
|
# VULNS_XSD = File.join(DATA_DIR, 'vuln.xsd')
|
||||||
WP_VERSIONS_XSD = DATA_DIR + '/wp_versions.xsd'
|
WP_VERSIONS_XSD = File.join(DATA_DIR, 'wp_versions.xsd')
|
||||||
LOCAL_FILES_XSD = DATA_DIR + '/local_vulnerable_files.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.2'
|
WPSCAN_VERSION = '2.6'
|
||||||
|
|
||||||
$LOAD_PATH.unshift(LIB_DIR)
|
$LOAD_PATH.unshift(LIB_DIR)
|
||||||
$LOAD_PATH.unshift(WPSCAN_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')
|
def require_files_from_directory(absolute_dir_path, files_pattern = '*.rb')
|
||||||
files = Dir[File.join(absolute_dir_path, files_pattern)]
|
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|
|
files.sort_by { |file| [file.count("/"), file] }.each do |f|
|
||||||
f = File.expand_path(f)
|
f = File.expand_path(f)
|
||||||
#puts "require #{f}" # Used for debug
|
#puts "require #{f}" # Used for debug
|
||||||
@@ -72,18 +73,56 @@ def add_trailing_slash(url)
|
|||||||
url =~ /\/$/ ? url : "#{url}/"
|
url =~ /\/$/ ? url : "#{url}/"
|
||||||
end
|
end
|
||||||
|
|
||||||
# loading the updater
|
def missing_db_file?
|
||||||
require_files_from_directory(UPDATER_LIB_DIR)
|
DbUpdater::FILES.each do |db_file|
|
||||||
@updater = UpdaterFactory.get_updater(ROOT_DIR)
|
return true unless File.exist?(File.join(DATA_DIR, db_file))
|
||||||
|
end
|
||||||
if @updater
|
false
|
||||||
REVISION = @updater.local_revision_number()
|
|
||||||
else
|
|
||||||
REVISION = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def version
|
# Define colors
|
||||||
REVISION ? "v#{WPSCAN_VERSION}r#{REVISION}" : "v#{WPSCAN_VERSION}"
|
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
|
end
|
||||||
|
|
||||||
# our 1337 banner
|
# our 1337 banner
|
||||||
@@ -97,35 +136,30 @@ def banner
|
|||||||
puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|'
|
puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|'
|
||||||
puts
|
puts
|
||||||
puts ' WordPress Security Scanner by the WPScan Team '
|
puts ' WordPress Security Scanner by the WPScan Team '
|
||||||
if REVISION
|
puts " Version #{WPSCAN_VERSION}"
|
||||||
puts " Version #{version}"
|
puts ' Sponsored by Sucuri - https://sucuri.net'
|
||||||
else
|
puts ' @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_'
|
||||||
puts " Version #{version}"
|
|
||||||
end
|
|
||||||
puts ' Sponsored by the RandomStorm Open Source Initiative'
|
|
||||||
puts ' @_WPScan_, @ethicalhack3r, @erwan_lr, @gbrindisi, @_FireFart_'
|
|
||||||
puts '_______________________________________________________________'
|
puts '_______________________________________________________________'
|
||||||
puts
|
puts
|
||||||
end
|
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)
|
def xml(file)
|
||||||
Nokogiri::XML(File.open(file)) do |config|
|
Nokogiri::XML(File.open(file)) do |config|
|
||||||
config.noblanks
|
config.noblanks
|
||||||
end
|
end
|
||||||
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)
|
def redefine_constant(constant, value)
|
||||||
Object.send(:remove_const, constant)
|
Object.send(:remove_const, constant)
|
||||||
Object.const_set(constant, value)
|
Object.const_set(constant, value)
|
||||||
@@ -177,3 +211,35 @@ end
|
|||||||
def count_file_lines(file)
|
def count_file_lines(file)
|
||||||
`wc -l #{file.shellescape}`.split[0].to_i
|
`wc -l #{file.shellescape}`.split[0].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Truncates a string to a specific length and adds ... at the end
|
||||||
|
def truncate(input, size, trailing = '...')
|
||||||
|
size = size.to_i
|
||||||
|
trailing ||= ''
|
||||||
|
return input if input.nil? or size <= 0 or input.length <= size or
|
||||||
|
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
|
||||||
|
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.open(file_path, 'wb') { |f| f.write(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
|
||||||
@@ -49,11 +49,11 @@ end
|
|||||||
|
|
||||||
# Override for puts to enable logging
|
# Override for puts to enable logging
|
||||||
def puts(o = '')
|
def puts(o = '')
|
||||||
# remove color for logging
|
if $log && o.respond_to?(:gsub)
|
||||||
if o.respond_to?(:gsub)
|
temp = o.gsub(/\e\[\d+m/, '') # remove color for logging
|
||||||
temp = o.gsub(/\e\[\d+m(.*)?\e\[0m/, '\1')
|
|
||||||
File.open(LOG_FILE, 'a+') { |f| f.puts(temp) }
|
File.open(LOG_FILE, 'a+') { |f| f.puts(temp) }
|
||||||
end
|
end
|
||||||
|
|
||||||
super(o)
|
super(o)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ require 'vulnerability/urls'
|
|||||||
|
|
||||||
class Vulnerability
|
class Vulnerability
|
||||||
include Vulnerability::Output
|
include Vulnerability::Output
|
||||||
include Vulnerability::Urls
|
include Vulnerability::Urls
|
||||||
|
|
||||||
attr_accessor :title, :references, :type, :fixed_in
|
attr_accessor :title, :references, :type, :fixed_in
|
||||||
|
|
||||||
@@ -35,27 +35,26 @@ class Vulnerability
|
|||||||
end
|
end
|
||||||
# :nocov:
|
# :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 ]
|
# @return [ Vulnerability ]
|
||||||
def self.load_from_xml_node(xml_node)
|
def self.load_from_json_item(json_item)
|
||||||
references = {}
|
references = {}
|
||||||
refs = xml_node.search('references')
|
|
||||||
if refs
|
%w(id url cve secunia osvdb metasploit exploitdb).each do |key|
|
||||||
references[:url] = refs.search('url').map(&:text)
|
if json_item[key]
|
||||||
references[:cve] = refs.search('cve').map(&:text)
|
json_item[key] = [json_item[key]] if json_item[key].class != Array
|
||||||
references[:secunia] = refs.search('secunia').map(&:text)
|
references[key] = json_item[key]
|
||||||
references[:osvdb] = refs.search('osvdb').map(&:text)
|
end
|
||||||
references[:metasploit] = refs.search('metasploit').map(&:text)
|
end
|
||||||
references[:exploitdb] = refs.search('exploitdb').map(&:text)
|
|
||||||
end
|
|
||||||
new(
|
new(
|
||||||
xml_node.search('title').text,
|
json_item['title'],
|
||||||
xml_node.search('type').text,
|
json_item['type'],
|
||||||
references,
|
references,
|
||||||
xml_node.search('fixed_in').text,
|
json_item['fixed_in'],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ class Vulnerability
|
|||||||
module Output
|
module Output
|
||||||
|
|
||||||
# output the vulnerability
|
# output the vulnerability
|
||||||
def output
|
def output(verbose = false)
|
||||||
puts ' |'
|
puts
|
||||||
puts ' | ' + red("* Title: #{title}")
|
puts "#{critical('[!]')} Title: #{title}"
|
||||||
references.each do |key, urls|
|
references.each do |key, urls|
|
||||||
methodname = "url_#{key}"
|
methodname = "url_#{key}"
|
||||||
urls.each do |u|
|
urls.each do |u|
|
||||||
url = send(methodname, u)
|
url = send(methodname, u)
|
||||||
puts ' | ' + red("* Reference: #{url}") if url
|
puts " Reference: #{url}" if url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if !fixed_in.empty?
|
if !fixed_in.nil?
|
||||||
puts " | * Fixed in: #{fixed_in}"
|
puts "#{notice('[i]')} Fixed in: #{fixed_in}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,33 +1,36 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
class Vulnerability
|
class Vulnerability
|
||||||
module Urls
|
module Urls
|
||||||
# @return [ String ] The url to the metasploit module page
|
# @return [ String ] The url to the metasploit module page
|
||||||
def url_metasploit(module_path)
|
def url_metasploit(module_path)
|
||||||
# remove leading slash
|
# remove leading slash
|
||||||
module_path = module_path.sub(/^\//, '')
|
module_path = module_path.sub(/^\//, '')
|
||||||
"http://www.metasploit.com/modules/#{module_path}"
|
"http://www.rapid7.com/db/modules/#{module_path}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_url(url)
|
def url_url(url)
|
||||||
url
|
url
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_cve(cve)
|
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
|
end
|
||||||
|
|
||||||
def url_osvdb(id)
|
def url_osvdb(id)
|
||||||
"http://osvdb.org/#{id}"
|
"http://osvdb.org/#{id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_secunia(id)
|
def url_secunia(id)
|
||||||
"http://secunia.com/advisories/#{id}"
|
"https://secunia.com/advisories/#{id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_exploitdb(id)
|
def url_exploitdb(id)
|
||||||
"http://www.exploit-db.com/exploits/#{id}/"
|
"http://www.exploit-db.com/exploits/#{id}/"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
def url_id(id)
|
||||||
end
|
"https://wpvulndb.com/vulnerabilities/#{id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ class WpItem
|
|||||||
#
|
#
|
||||||
# @return [ Boolean ]
|
# @return [ Boolean ]
|
||||||
def exists_from_response?(response, options = {})
|
def exists_from_response?(response, options = {})
|
||||||
if [200, 401, 403].include?(response.code)
|
# 301 included as some items do a self-redirect
|
||||||
|
# Redirects to the 404 and homepage should be ignored (unless dynamic content is used)
|
||||||
|
# by the page hashes (error_404_hash & homepage_hash)
|
||||||
|
if [200, 401, 403, 301].include?(response.code)
|
||||||
if response.has_valid_hash?(options[:error_404_hash], options[:homepage_hash])
|
if response.has_valid_hash?(options[:error_404_hash], options[:homepage_hash])
|
||||||
if options[:exclude_content]
|
if options[:exclude_content]
|
||||||
unless response.body.match(options[:exclude_content])
|
unless response.body.match(options[:exclude_content])
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ class WpItem
|
|||||||
|
|
||||||
# @return [ String,nil ] The url to the readme file, nil if not found
|
# @return [ String,nil ] The url to the readme file, nil if not found
|
||||||
def readme_url
|
def readme_url
|
||||||
%w{readme.txt README.txt}.each do |readme|
|
# See https://github.com/wpscanteam/wpscan/pull/737#issuecomment-66375445
|
||||||
|
# for any question about the order
|
||||||
|
%w{readme.txt README.txt Readme.txt ReadMe.txt README.TXT readme.TXT}.each do |readme|
|
||||||
url = @uri.merge(readme).to_s
|
url = @uri.merge(readme).to_s
|
||||||
return url if url_is_200?(url)
|
return url if url_is_200?(url)
|
||||||
end
|
end
|
||||||
@@ -40,7 +42,7 @@ class WpItem
|
|||||||
|
|
||||||
# @return [ Boolean ]
|
# @return [ Boolean ]
|
||||||
def has_directory_listing?
|
def has_directory_listing?
|
||||||
Browser.get(@uri.to_s).body[%r{<title>Index of}] ? true : false
|
directory_listing_enabled?(@uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Discover any error_log files created by WordPress
|
# Discover any error_log files created by WordPress
|
||||||
|
|||||||
@@ -4,21 +4,24 @@ class WpItem
|
|||||||
module Output
|
module Output
|
||||||
|
|
||||||
# @return [ Void ]
|
# @return [ Void ]
|
||||||
def output
|
def output(verbose = false)
|
||||||
puts
|
puts
|
||||||
puts " | Name: #{self}" #this will also output the version number if detected
|
puts "#{info('[+]')} Name: #{self}" #this will also output the version number if detected
|
||||||
puts " | Location: #{url}"
|
puts " | Location: #{url}"
|
||||||
#puts " | WordPress: #{wordpress_url}" if wordpress_org_item?
|
#puts " | WordPress: #{wordpress_url}" if wordpress_org_item?
|
||||||
puts ' | Directory listing enabled: Yes' if has_directory_listing?
|
puts " | Readme: #{readme_url}" if has_readme?
|
||||||
puts " | Readme: #{readme_url}" if has_readme?
|
puts " | Changelog: #{changelog_url}" if has_changelog?
|
||||||
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
|
vulnerabilities.output
|
||||||
|
|
||||||
if has_error_log?
|
|
||||||
puts ' | ' + red('[!]') + " An error_log file has been found : #{error_log_url}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class WpItem
|
|||||||
# This check is needed because readme_url can return nil
|
# This check is needed because readme_url can return nil
|
||||||
if has_readme?
|
if has_readme?
|
||||||
response = Browser.get(readme_url)
|
response = Browser.get(readme_url)
|
||||||
@version = response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
|
@version = response.body[%r{(?:stable tag|version): #{WpVersion.version_pattern}}i, 1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@version
|
@version
|
||||||
@@ -22,7 +22,7 @@ class WpItem
|
|||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def to_s
|
def to_s
|
||||||
item_version = self.version
|
item_version = self.version
|
||||||
"#@name#{' v' + item_version.strip if item_version}"
|
"#@name#{' - v' + item_version.strip if item_version}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,22 +2,27 @@
|
|||||||
|
|
||||||
class WpItem
|
class WpItem
|
||||||
module Vulnerable
|
module Vulnerable
|
||||||
attr_accessor :vulns_file, :vulns_xpath
|
attr_accessor :vulns_file, :identifier
|
||||||
|
|
||||||
# Get the vulnerabilities associated to the WpItem
|
# Get the vulnerabilities associated to the WpItem
|
||||||
# Filters out already fixed vulnerabilities
|
# Filters out already fixed vulnerabilities
|
||||||
#
|
#
|
||||||
# @return [ Vulnerabilities ]
|
# @return [ Vulnerabilities ]
|
||||||
def vulnerabilities
|
def vulnerabilities
|
||||||
xml = xml(vulns_file)
|
json = json(vulns_file)
|
||||||
vulnerabilities = Vulnerabilities.new
|
vulnerabilities = Vulnerabilities.new
|
||||||
|
|
||||||
xml.xpath(vulns_xpath).each do |node|
|
json.each do |item|
|
||||||
vuln = Vulnerability.load_from_xml_node(node)
|
asset = item[identifier]
|
||||||
if vulnerable_to?(vuln)
|
|
||||||
vulnerabilities << vuln
|
if asset
|
||||||
|
asset['vulnerabilities'].each do |vulnerability|
|
||||||
|
vulnerability = Vulnerability.load_from_json_item(vulnerability)
|
||||||
|
vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
vulnerabilities
|
vulnerabilities
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -32,7 +37,7 @@ class WpItem
|
|||||||
# @return [ Boolean ]
|
# @return [ Boolean ]
|
||||||
def vulnerable_to?(vuln)
|
def vulnerable_to?(vuln)
|
||||||
if version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
|
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
|
return true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -41,5 +46,4 @@ class WpItem
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ class WpPlugin < WpItem
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def vulns_xpath
|
def identifier
|
||||||
"//plugin[@name='#{@name}']/vulnerability"
|
@name
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,15 +3,21 @@
|
|||||||
require 'wp_theme/findable'
|
require 'wp_theme/findable'
|
||||||
require 'wp_theme/versionable'
|
require 'wp_theme/versionable'
|
||||||
require 'wp_theme/vulnerable'
|
require 'wp_theme/vulnerable'
|
||||||
|
require 'wp_theme/info'
|
||||||
|
require 'wp_theme/output'
|
||||||
|
require 'wp_theme/childtheme'
|
||||||
|
|
||||||
class WpTheme < WpItem
|
class WpTheme < WpItem
|
||||||
extend WpTheme::Findable
|
extend WpTheme::Findable
|
||||||
include WpTheme::Versionable
|
include WpTheme::Versionable
|
||||||
include WpTheme::Vulnerable
|
include WpTheme::Vulnerable
|
||||||
|
include WpTheme::Info
|
||||||
|
include WpTheme::Output
|
||||||
|
include WpTheme::Childtheme
|
||||||
|
|
||||||
attr_writer :style_url
|
attr_accessor :referenced_url
|
||||||
|
|
||||||
def allowed_options; super << :style_url end
|
def allowed_options; super << :referenced_url end
|
||||||
|
|
||||||
# Sets the @uri
|
# Sets the @uri
|
||||||
#
|
#
|
||||||
@@ -24,10 +30,7 @@ class WpTheme < WpItem
|
|||||||
|
|
||||||
# @return [ String ] The url to the theme stylesheet
|
# @return [ String ] The url to the theme stylesheet
|
||||||
def style_url
|
def style_url
|
||||||
unless @style_url
|
@uri.merge('style.css').to_s
|
||||||
@style_url = uri.merge('style.css').to_s
|
|
||||||
end
|
|
||||||
@style_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
37
lib/common/models/wp_theme/childtheme.rb
Normal file
37
lib/common/models/wp_theme/childtheme.rb
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTheme < WpItem
|
||||||
|
module Childtheme
|
||||||
|
|
||||||
|
def parent_theme_limit
|
||||||
|
3
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_child_theme?
|
||||||
|
return true unless @theme_template.nil?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_parent_theme_style_url
|
||||||
|
if is_child_theme?
|
||||||
|
return style_url.sub("/#{name}/style.css", "/#@theme_template/style.css")
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_parent_theme
|
||||||
|
if is_child_theme?
|
||||||
|
base_url = @uri.clone
|
||||||
|
base_url.path = base_url.path.sub(/(?<url>.*\/)#{Regexp.escape(@wp_content_dir)}\/.+/, '\k<url>')
|
||||||
|
return WpTheme.new(base_url,
|
||||||
|
{
|
||||||
|
name: @theme_template,
|
||||||
|
style_url: get_parent_theme_style_url,
|
||||||
|
wp_content_dir: @wp_content_dir
|
||||||
|
})
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -30,21 +30,19 @@ class WpTheme < WpItem
|
|||||||
response = Browser.get_and_follow_location(target_uri.to_s)
|
response = Browser.get_and_follow_location(target_uri.to_s)
|
||||||
|
|
||||||
# https + domain is optional because of relative links
|
# 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
|
if matches
|
||||||
return new(
|
return new(
|
||||||
target_uri,
|
target_uri,
|
||||||
{
|
{
|
||||||
name: matches[2],
|
name: matches[2],
|
||||||
style_url: matches[0],
|
referenced_url: matches[0],
|
||||||
wp_content_dir: matches[1]
|
wp_content_dir: matches[1]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# http://code.google.com/p/wpscan/issues/detail?id=141
|
|
||||||
#
|
|
||||||
# @param [ URI ] target_uri
|
# @param [ URI ] target_uri
|
||||||
#
|
#
|
||||||
# @return [ WpTheme ]
|
# @return [ WpTheme ]
|
||||||
|
|||||||
34
lib/common/models/wp_theme/info.rb
Normal file
34
lib/common/models/wp_theme/info.rb
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTheme < WpItem
|
||||||
|
module Info
|
||||||
|
|
||||||
|
attr_reader :theme_name, :theme_uri, :theme_description,
|
||||||
|
:theme_author, :theme_author_uri, :theme_template,
|
||||||
|
:theme_license, :theme_license_uri, :theme_tags,
|
||||||
|
:theme_text_domain
|
||||||
|
|
||||||
|
def parse_style
|
||||||
|
style = Browser.get(style_url).body
|
||||||
|
@theme_name = parse_style_tag(style, 'Theme Name')
|
||||||
|
@theme_uri = parse_style_tag(style, 'Theme URI')
|
||||||
|
@theme_description = parse_style_tag(style, 'Description')
|
||||||
|
@theme_author = parse_style_tag(style, 'Author')
|
||||||
|
@theme_author_uri = parse_style_tag(style, 'Author URI')
|
||||||
|
@theme_template = parse_style_tag(style, 'Template')
|
||||||
|
@theme_license = parse_style_tag(style, 'License')
|
||||||
|
@theme_license_uri = parse_style_tag(style, 'License URI')
|
||||||
|
@theme_tags = parse_style_tag(style, 'Tags')
|
||||||
|
@theme_text_domain = parse_style_tag(style, 'Text Domain')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_style_tag(style, tag)
|
||||||
|
value = style[/^\s*#{Regexp.escape(tag)}:\s*(.*)/i, 1]
|
||||||
|
return value.strip if value
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
26
lib/common/models/wp_theme/output.rb
Normal file
26
lib/common/models/wp_theme/output.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTheme
|
||||||
|
module Output
|
||||||
|
|
||||||
|
# @return [ Void ]
|
||||||
|
def additional_output(verbose = false)
|
||||||
|
parse_style
|
||||||
|
|
||||||
|
theme_desc = verbose ? @theme_description : truncate(@theme_description, 100)
|
||||||
|
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
|
||||||
|
end
|
||||||
@@ -2,16 +2,8 @@
|
|||||||
|
|
||||||
class WpTheme < WpItem
|
class WpTheme < WpItem
|
||||||
module Versionable
|
module Versionable
|
||||||
|
|
||||||
def version
|
def version
|
||||||
unless @version
|
@version ||= Browser.get(style_url).body[%r{Version:\s*([^\s]+)}i, 1]
|
||||||
@version = Browser.get(style_url).body[%r{Version:\s([^\s]+)}i, 1]
|
|
||||||
|
|
||||||
# Get Version from readme.txt
|
|
||||||
@version ||= super
|
|
||||||
end
|
|
||||||
@version
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ class WpTheme < WpItem
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def vulns_xpath
|
def identifier
|
||||||
"//theme[@name='#{@name}']/vulnerability"
|
@name
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
require 'wp_timthumb/versionable'
|
require 'wp_timthumb/versionable'
|
||||||
require 'wp_timthumb/existable'
|
require 'wp_timthumb/existable'
|
||||||
require 'wp_timthumb/output'
|
require 'wp_timthumb/output'
|
||||||
|
require 'wp_timthumb/vulnerable'
|
||||||
|
|
||||||
class WpTimthumb < WpItem
|
class WpTimthumb < WpItem
|
||||||
include WpTimthumb::Versionable
|
include WpTimthumb::Versionable
|
||||||
include WpTimthumb::Existable
|
include WpTimthumb::Existable
|
||||||
include WpTimthumb::Output
|
include WpTimthumb::Output
|
||||||
|
include WpTimthumb::Vulnerable
|
||||||
|
|
||||||
# @param [ WpTimthumb ] other
|
# @param [ WpTimthumb ] other
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
class WpTimthumb < WpItem
|
class WpTimthumb < WpItem
|
||||||
module Output
|
module Output
|
||||||
|
|
||||||
def 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
|
||||||
|
|
||||||
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> ]
|
# @return [ Array<Symbol> ]
|
||||||
def allowed_options; [:id, :login, :display_name, :password] end
|
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
|
def uri
|
||||||
if id
|
if id
|
||||||
return @uri.merge("?author=#{id}")
|
return @uri.merge("?author=#{id}")
|
||||||
@@ -23,14 +23,39 @@ class WpUser < WpItem
|
|||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def login_url
|
def login_url
|
||||||
@uri.merge('wp-login.php').to_s
|
unless @login_url
|
||||||
|
@login_url = @uri.merge('wp-login.php').to_s
|
||||||
|
|
||||||
|
# Let's check if the login url is redirected (to https url for example)
|
||||||
|
if redirection = redirection(@login_url)
|
||||||
|
@login_url = redirection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@login_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def redirection(url)
|
||||||
|
redirection = nil
|
||||||
|
response = Browser.get(url)
|
||||||
|
|
||||||
|
if response.code == 301 || response.code == 302
|
||||||
|
redirection = response.headers_hash['location']
|
||||||
|
|
||||||
|
# Let's check if there is a redirection in the redirection
|
||||||
|
if other_redirection = redirection(redirection)
|
||||||
|
redirection = other_redirection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
redirection
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def to_s
|
def to_s
|
||||||
s = "#{id}"
|
s = "#{id}"
|
||||||
s += " | #{login}" if login
|
s << " | #{login}" if login
|
||||||
s += " | #{display_name}" if display_name
|
s << " | #{display_name}" if display_name
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ class WpUser < WpItem
|
|||||||
hydra = browser.hydra
|
hydra = browser.hydra
|
||||||
queue_count = 0
|
queue_count = 0
|
||||||
found = false
|
found = false
|
||||||
progress_bar = self.progress_bar(count_file_lines(wordlist), options)
|
progress_bar = self.progress_bar(count_file_lines(wordlist)+1, options)
|
||||||
|
|
||||||
File.open(wordlist).each do |password|
|
File.open(wordlist).each do |password|
|
||||||
password.chop!
|
password.chomp!
|
||||||
|
|
||||||
# A successfull login will redirect us to the redirect_to parameter
|
# A successfull login will redirect us to the redirect_to parameter
|
||||||
# Generate a random one on each request
|
# Generate a random one on each request
|
||||||
unless redirect_url
|
unless redirect_url
|
||||||
@@ -63,6 +63,7 @@ class WpUser < WpItem
|
|||||||
|
|
||||||
# run all of the remaining requests
|
# run all of the remaining requests
|
||||||
hydra.run
|
hydra.run
|
||||||
|
puts if options[:show_progression] # mandatory to avoid the output of the progressbar to be overriden
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param [ Integer ] targets_size
|
# @param [ Integer ] targets_size
|
||||||
@@ -103,19 +104,19 @@ class WpUser < WpItem
|
|||||||
# @return [ Boolean ]
|
# @return [ Boolean ]
|
||||||
def valid_password?(response, password, redirect_url, options = {})
|
def valid_password?(response, password, redirect_url, options = {})
|
||||||
if response.code == 302 && response.headers_hash && response.headers_hash['Location'] == redirect_url
|
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
|
valid = true
|
||||||
elsif response.body =~ /login_error/i
|
elsif response.body =~ /login_error/i
|
||||||
verbose = "\n Incorrect login and/or password."
|
verbose = "\n Incorrect login and/or password."
|
||||||
elsif response.timed_out?
|
elsif response.timed_out?
|
||||||
progression = "#{red('ERROR:')} Request timed out."
|
progression = "#{critical('ERROR:')} Request timed out."
|
||||||
elsif response.code == 0
|
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/
|
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
|
else
|
||||||
progression = "#{red('ERROR:')} We received an unknown response for #{password}..."
|
progression = "#{critical('ERROR:')} We received an unknown response for #{password}..."
|
||||||
verbose = red(" Code: #{response.code}\n Body: #{response.body}\n")
|
verbose = critical(" Code: #{response.code}\n Body: #{response.body}\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "\n " + progression if progression && options[:show_progression]
|
puts "\n " + progression if progression && options[:show_progression]
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class WpUser < WpItem
|
|||||||
if response.code == 301 # login in location?
|
if response.code == 301 # login in location?
|
||||||
location = response.headers_hash['Location']
|
location = response.headers_hash['Location']
|
||||||
|
|
||||||
|
return if location.nil? || location.empty?
|
||||||
|
|
||||||
@login = Existable.login_from_author_pattern(location)
|
@login = Existable.login_from_author_pattern(location)
|
||||||
@display_name = Existable.display_name_from_body(
|
@display_name = Existable.display_name_from_body(
|
||||||
Browser.get(location).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.force_encoding('UTF-8') if title_tag.encoding == Encoding::ASCII_8BIT
|
||||||
title_tag = Nokogiri::HTML::DocumentFragment.parse(title_tag).to_s
|
title_tag = Nokogiri::HTML::DocumentFragment.parse(title_tag).to_s
|
||||||
# & are not decoded with Nokogiri
|
# & 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
|
return name.strip if name
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class WpVersion < WpItem
|
|||||||
#
|
#
|
||||||
# @return [ WpVersion ]
|
# @return [ WpVersion ]
|
||||||
def find(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
|
def find(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
|
||||||
methods.grep(/find_from_/).each do |method|
|
methods.grep(/^find_from_/).each do |method|
|
||||||
|
|
||||||
if method === :find_from_advanced_fingerprinting
|
if method === :find_from_advanced_fingerprinting
|
||||||
version = send(method, target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
|
version = send(method, target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
|
||||||
@@ -100,18 +100,6 @@ class WpVersion < WpItem
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempts to find the WordPress version from,
|
|
||||||
# the generator tag in the RSS2 feed source.
|
|
||||||
#
|
|
||||||
# Have not been able to find an example of this - Ryan
|
|
||||||
#def find_from_rss2_generator(target_uri)
|
|
||||||
# scan_url(
|
|
||||||
# target_uri,
|
|
||||||
# %r{<generator>http://wordpress.org/?v=(#{WpVersion.version_pattern})</generator>}i,
|
|
||||||
# 'feed/rss/'
|
|
||||||
# )
|
|
||||||
#end
|
|
||||||
|
|
||||||
# Attempts to find the WordPress version from,
|
# Attempts to find the WordPress version from,
|
||||||
# the generator tag in the Atom source.
|
# the generator tag in the Atom source.
|
||||||
#
|
#
|
||||||
@@ -126,18 +114,6 @@ class WpVersion < WpItem
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempts to find the WordPress version from,
|
|
||||||
# the generator tag in the comment rss source.
|
|
||||||
#
|
|
||||||
# Have not been able to find an example of this - Ryan
|
|
||||||
#def find_from_comments_rss_generator(target_uri)
|
|
||||||
# scan_url(
|
|
||||||
# target_uri,
|
|
||||||
# %r{<!-- generator="WordPress/#{WpVersion.version_pattern}" -->}i,
|
|
||||||
# 'comments/feed/'
|
|
||||||
# )
|
|
||||||
#end
|
|
||||||
|
|
||||||
# Uses data/wp_versions.xml to try to identify a
|
# Uses data/wp_versions.xml to try to identify a
|
||||||
# wordpress version.
|
# wordpress version.
|
||||||
#
|
#
|
||||||
@@ -190,8 +166,6 @@ class WpVersion < WpItem
|
|||||||
|
|
||||||
# Attempts to find the WordPress version from the sitemap.xml file.
|
# 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
|
# @param [ URI ] target_uri
|
||||||
#
|
#
|
||||||
# @return [ String ] The version number
|
# @return [ String ] The version number
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
class WpVersion < WpItem
|
class WpVersion < WpItem
|
||||||
module Output
|
module Output
|
||||||
|
|
||||||
def output
|
def output(verbose = false)
|
||||||
puts green('[+]') + " WordPress version #{self.number} identified from #{self.found_from}"
|
puts
|
||||||
|
puts "#{info('[+]')} WordPress version #{self.number} identified from #{self.found_from}"
|
||||||
|
|
||||||
vulnerabilities = self.vulnerabilities
|
vulnerabilities = self.vulnerabilities
|
||||||
|
|
||||||
unless vulnerabilities.empty?
|
unless vulnerabilities.empty?
|
||||||
puts
|
puts "#{critical('[!]')} #{vulnerabilities.size} vulnerabilities identified from the version number"
|
||||||
puts red('[!]') + " #{vulnerabilities.size} vulnerabilities identified from the version number:"
|
|
||||||
|
|
||||||
vulnerabilities.output
|
vulnerabilities.output
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,9 +12,14 @@ class WpVersion < WpItem
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def vulns_xpath
|
def identifier
|
||||||
"//wordpress[@version='#{@number}']/vulnerability"
|
@number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [ String ]
|
||||||
|
# def vulns_xpath
|
||||||
|
# "//wordpress[@version='#{@number}']/vulnerability"
|
||||||
|
# end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,25 +2,14 @@
|
|||||||
|
|
||||||
require 'common/cache_file_store'
|
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
|
class TyphoeusCache < CacheFileStore
|
||||||
|
|
||||||
def get(request)
|
def get(request)
|
||||||
read_entry(request.cache_key)
|
read_entry(request.hash.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
def set(request, response)
|
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
|
||||||
|
|
||||||
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
|
class VersionCompare
|
||||||
|
|
||||||
# Compares two version strings. Returns true if version1 is equal to version2
|
# Compares two version strings. Returns true if version1 <= version2
|
||||||
# or when version1 is older than version2
|
# and false otherwise
|
||||||
#
|
#
|
||||||
# @param [ String ] version1
|
# @param [ String ] version1
|
||||||
# @param [ String ] version2
|
# @param [ String ] version2
|
||||||
#
|
#
|
||||||
# @return [ Boolean ]
|
# @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)
|
return true if (version1 == version2)
|
||||||
# Both versions must be set
|
# Both versions must be set
|
||||||
return false unless (version1 and version2)
|
return false unless (version1 and version2)
|
||||||
@@ -22,5 +26,5 @@ class VersionCompare
|
|||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ begin
|
|||||||
require 'pp'
|
require 'pp'
|
||||||
require 'shellwords'
|
require 'shellwords'
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
|
require 'pathname'
|
||||||
# Third party libs
|
# Third party libs
|
||||||
require 'typhoeus'
|
require 'typhoeus'
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class WebSite
|
|||||||
|
|
||||||
def has_xml_rpc?
|
def has_xml_rpc?
|
||||||
response = Browser.get_and_follow_location(xml_rpc_url)
|
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
|
end
|
||||||
|
|
||||||
# See http://www.hixie.ch/specs/pingback/pingback-1.0#TOC2.3
|
# See http://www.hixie.ch/specs/pingback/pingback-1.0#TOC2.3
|
||||||
@@ -52,8 +52,12 @@ class WebSite
|
|||||||
url ||= @uri.to_s
|
url ||= @uri.to_s
|
||||||
response = Browser.get(url)
|
response = Browser.get(url)
|
||||||
|
|
||||||
|
redirected_uri = URI.parse(add_trailing_slash(add_http_protocol(url)))
|
||||||
if response.code == 301 || response.code == 302
|
if response.code == 301 || response.code == 302
|
||||||
redirection = response.headers_hash['location']
|
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
|
# Let's check if there is a redirection in the redirection
|
||||||
if other_redirection = redirection(redirection)
|
if other_redirection = redirection(redirection)
|
||||||
@@ -71,7 +75,7 @@ class WebSite
|
|||||||
#
|
#
|
||||||
# @return [ String ] The MD5 hash of the page
|
# @return [ String ] The MD5 hash of the page
|
||||||
def self.page_hash(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, ''))
|
Digest::MD5.hexdigest(page.body.gsub(/<!--.*?-->/m, ''))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ class WebSite
|
|||||||
# Gets a robots.txt URL
|
# Gets a robots.txt URL
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def robots_url
|
def robots_url
|
||||||
temp = @uri.clone
|
@uri.clone.merge('robots.txt').to_s
|
||||||
temp.path = '/robots.txt'
|
|
||||||
temp.to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
require 'web_site'
|
require 'web_site'
|
||||||
require 'wp_target/malwares'
|
|
||||||
require 'wp_target/wp_readme'
|
require 'wp_target/wp_readme'
|
||||||
require 'wp_target/wp_registrable'
|
require 'wp_target/wp_registrable'
|
||||||
require 'wp_target/wp_config_backup'
|
require 'wp_target/wp_config_backup'
|
||||||
|
require 'wp_target/wp_must_use_plugins'
|
||||||
require 'wp_target/wp_login_protection'
|
require 'wp_target/wp_login_protection'
|
||||||
require 'wp_target/wp_custom_directories'
|
require 'wp_target/wp_custom_directories'
|
||||||
require 'wp_target/wp_full_path_disclosure'
|
require 'wp_target/wp_full_path_disclosure'
|
||||||
|
|
||||||
class WpTarget < WebSite
|
class WpTarget < WebSite
|
||||||
include WpTarget::Malwares
|
|
||||||
include WpTarget::WpReadme
|
include WpTarget::WpReadme
|
||||||
include WpTarget::WpRegistrable
|
include WpTarget::WpRegistrable
|
||||||
include WpTarget::WpConfigBackup
|
include WpTarget::WpConfigBackup
|
||||||
|
include WpTarget::WpMustUsePlugins
|
||||||
include WpTarget::WpLoginProtection
|
include WpTarget::WpLoginProtection
|
||||||
include WpTarget::WpCustomDirectories
|
include WpTarget::WpCustomDirectories
|
||||||
include WpTarget::WpFullPathDisclosure
|
include WpTarget::WpFullPathDisclosure
|
||||||
@@ -28,7 +28,7 @@ class WpTarget < WebSite
|
|||||||
@wp_plugins_dir = options[:wp_plugins_dir]
|
@wp_plugins_dir = options[:wp_plugins_dir]
|
||||||
@multisite = nil
|
@multisite = nil
|
||||||
|
|
||||||
Browser.instance(options.merge(:max_threads => options[:threads]))
|
Browser.instance.referer = url
|
||||||
end
|
end
|
||||||
|
|
||||||
# check if the target website is
|
# check if the target website is
|
||||||
@@ -38,6 +38,11 @@ class WpTarget < WebSite
|
|||||||
|
|
||||||
response = Browser.get_and_follow_location(@uri.to_s)
|
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
|
if response.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i
|
||||||
wordpress = true
|
wordpress = true
|
||||||
else
|
else
|
||||||
@@ -93,7 +98,7 @@ class WpTarget < WebSite
|
|||||||
end
|
end
|
||||||
# :nocov:
|
# :nocov:
|
||||||
|
|
||||||
# The version is not yet considerated
|
# The version is not yet considered
|
||||||
#
|
#
|
||||||
# @param [ String ] name
|
# @param [ String ] name
|
||||||
# @param [ String ] version
|
# @param [ String ] version
|
||||||
@@ -116,11 +121,16 @@ class WpTarget < WebSite
|
|||||||
|
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
def debug_log_url
|
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
|
end
|
||||||
|
|
||||||
# Script for replacing strings in wordpress databases
|
# Script for replacing strings in wordpress databases
|
||||||
# reveals databse credentials after hitting submit
|
# reveals database credentials after hitting submit
|
||||||
# http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
|
# http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
|
||||||
#
|
#
|
||||||
# @return [ String ]
|
# @return [ String ]
|
||||||
@@ -133,4 +143,8 @@ class WpTarget < WebSite
|
|||||||
resp = Browser.get(search_replace_db_2_url)
|
resp = Browser.get(search_replace_db_2_url)
|
||||||
resp.code == 200 && resp.body[%r{by interconnect}i]
|
resp.code == 200 && resp.body[%r{by interconnect}i]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def upload_directory_listing_enabled?
|
||||||
|
directory_listing_enabled?(upload_dir_url)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
class WpTarget < WebSite
|
|
||||||
module Malwares
|
|
||||||
# Used as cache :
|
|
||||||
# nil => malwares not checked,
|
|
||||||
# [] => no malwares,
|
|
||||||
# otherwise array of malwares url found
|
|
||||||
@malwares = nil
|
|
||||||
|
|
||||||
def has_malwares?(malwares_file_path = nil)
|
|
||||||
!malwares(malwares_file_path).empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
# return array of string (url of malwares found)
|
|
||||||
def malwares(malwares_file_path = nil)
|
|
||||||
unless @malwares
|
|
||||||
malwares_found = []
|
|
||||||
malwares_file = Malwares.malwares_file(malwares_file_path)
|
|
||||||
index_page_body = Browser.get(@uri.to_s).body
|
|
||||||
|
|
||||||
File.open(malwares_file, 'r') do |file|
|
|
||||||
file.readlines.collect do |url|
|
|
||||||
chomped_url = url.chomp
|
|
||||||
|
|
||||||
if chomped_url.length > 0
|
|
||||||
malwares_found += index_page_body.scan(Malwares.malware_pattern(chomped_url))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
malwares_found.flatten!
|
|
||||||
malwares_found.uniq!
|
|
||||||
|
|
||||||
@malwares = malwares_found
|
|
||||||
end
|
|
||||||
@malwares
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.malwares_file(malwares_file_path)
|
|
||||||
malwares_file_path || DATA_DIR + '/malwares.txt'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.malware_pattern(url_regex)
|
|
||||||
# no need to escape regex here, because malware.txt contains regex
|
|
||||||
%r{<(?:script|iframe).* src=(?:"|')(#{url_regex}[^"']*)(?:"|')[^>]*>}i
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -40,9 +40,9 @@ class WpTarget < WebSite
|
|||||||
# @return [ Array ]
|
# @return [ Array ]
|
||||||
def self.config_backup_files
|
def self.config_backup_files
|
||||||
%w{
|
%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.php~ #wp-config.php# wp-config.php.save .wp-config.php.swp wp-config.php.swp wp-config.php.swo
|
||||||
wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old wp-config.php.orig
|
wp-config.php_bak wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old
|
||||||
wp-config.orig wp-config.php.original wp-config.original wp-config.txt
|
wp-config.php.orig wp-config.orig wp-config.php.original wp-config.original wp-config.txt
|
||||||
} # thanks to Feross.org for these
|
} # thanks to Feross.org for these
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class WpTarget < WebSite
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Checks if a login protection plugin is enabled
|
# 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
|
# return a WpPlugin object or nil if no one is found
|
||||||
def login_protection_plugin
|
def login_protection_plugin
|
||||||
unless @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 '-Use custom plugins directory ...'
|
||||||
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
||||||
puts
|
puts
|
||||||
puts '-Update ...'
|
puts '-Update the DB ...'
|
||||||
puts "ruby #{script_name} --update"
|
puts "ruby #{script_name} --update"
|
||||||
puts
|
puts
|
||||||
puts '-Debug output ...'
|
puts '-Debug output ...'
|
||||||
@@ -60,13 +60,12 @@ end
|
|||||||
def help
|
def help
|
||||||
puts 'Help :'
|
puts 'Help :'
|
||||||
puts
|
puts
|
||||||
puts 'Some values are settable in conf/browser.conf.json :'
|
puts 'Some values are settable in a config file, see the example.conf.json'
|
||||||
puts ' user-agent, proxy, proxy-auth, threads, cache timeout and request timeout'
|
|
||||||
puts
|
puts
|
||||||
puts '--update Update to the latest revision'
|
puts '--update Update to the database to the latest version.'
|
||||||
puts '--url | -u <target url> The WordPress URL/domain to scan.'
|
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 '--force | -f Forces WPScan to not check if the remote site is running WordPress.'
|
||||||
puts '--enumerate | -e [option(s)] Enumeration.'
|
puts '--enumerate | -e [option(s)] Enumeration.'
|
||||||
puts ' option :'
|
puts ' option :'
|
||||||
puts ' u usernames from id 1 to 10'
|
puts ' u usernames from id 1 to 10'
|
||||||
puts ' u[10-20] usernames from id 10 to 20 (you must write [] chars)'
|
puts ' u[10-20] usernames from id 10 to 20 (you must write [] chars)'
|
||||||
@@ -80,20 +79,42 @@ def help
|
|||||||
puts ' Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins'
|
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 ' If no option is supplied, the default is "vt,tt,u,vp"'
|
||||||
puts
|
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 '--exclude-content-based "<regexp or string>"'
|
||||||
puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)'
|
puts ' Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied.'
|
||||||
puts '--config-file | -c <config file> Use the specified config file'
|
puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).'
|
||||||
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 '--config-file | -c <config file> Use the specified config file, see the example.conf.json.'
|
||||||
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 '--user-agent | -a <User-Agent> Use the specified User-Agent.'
|
||||||
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 '--cookie <String> String to read cookies from.'
|
||||||
puts '--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).'
|
puts '--random-agent | -r Use a random User-Agent.'
|
||||||
puts ' HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used'
|
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 '--proxy-auth <username:password> Supply the proxy login credentials (will override the one from conf/browser.conf.json).'
|
puts '--batch Never ask for user input, use the default behaviour.'
|
||||||
puts '--basic-auth <username:password> Set the HTTP Basic authentication'
|
puts '--no-color Do not use colors in the output.'
|
||||||
puts '--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute.'
|
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 '--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 ' Subdirectories are allowed.'
|
||||||
puts '--username | -U <username> Only brute force the supplied username.'
|
puts '--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory.'
|
||||||
puts '--help | -h This help screen.'
|
puts ' If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed'
|
||||||
puts '--verbose | -v Verbose output.'
|
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 brute forcer.'
|
||||||
|
puts '--username | -U <username> Only brute force the supplied username.'
|
||||||
|
puts '--usernames <path-to-file> Only brute force the usernames from the file.'
|
||||||
|
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 '--version Output the current version and exit.'
|
||||||
puts
|
puts
|
||||||
end
|
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
|
class WpscanOptions
|
||||||
|
|
||||||
ACCESSOR_OPTIONS = [
|
ACCESSOR_OPTIONS = [
|
||||||
|
:batch,
|
||||||
:enumerate_plugins,
|
:enumerate_plugins,
|
||||||
:enumerate_only_vulnerable_plugins,
|
:enumerate_only_vulnerable_plugins,
|
||||||
:enumerate_all_plugins,
|
:enumerate_all_plugins,
|
||||||
@@ -12,6 +13,8 @@ class WpscanOptions
|
|||||||
:enumerate_timthumbs,
|
:enumerate_timthumbs,
|
||||||
:enumerate_usernames,
|
:enumerate_usernames,
|
||||||
:enumerate_usernames_range,
|
:enumerate_usernames_range,
|
||||||
|
:no_color,
|
||||||
|
:log,
|
||||||
:proxy,
|
:proxy,
|
||||||
:proxy_auth,
|
:proxy_auth,
|
||||||
:threads,
|
:threads,
|
||||||
@@ -21,16 +24,24 @@ class WpscanOptions
|
|||||||
:update,
|
:update,
|
||||||
:verbose,
|
:verbose,
|
||||||
:username,
|
:username,
|
||||||
|
:usernames,
|
||||||
:password,
|
:password,
|
||||||
:follow_redirection,
|
:follow_redirection,
|
||||||
:wp_content_dir,
|
:wp_content_dir,
|
||||||
:wp_plugins_dir,
|
:wp_plugins_dir,
|
||||||
:help,
|
:help,
|
||||||
:config_file,
|
:config_file,
|
||||||
|
:cookie,
|
||||||
:exclude_content_based,
|
:exclude_content_based,
|
||||||
:basic_auth,
|
:basic_auth,
|
||||||
:debug_output,
|
:debug_output,
|
||||||
:version
|
:version,
|
||||||
|
:user_agent,
|
||||||
|
:random_agent,
|
||||||
|
:cache_ttl,
|
||||||
|
:request_timeout,
|
||||||
|
:connect_timeout,
|
||||||
|
:max_threads
|
||||||
]
|
]
|
||||||
|
|
||||||
attr_accessor *ACCESSOR_OPTIONS
|
attr_accessor *ACCESSOR_OPTIONS
|
||||||
@@ -59,6 +70,12 @@ class WpscanOptions
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def usernames=(file)
|
||||||
|
fail "The file #{file} does not exist" unless File.exists?(file)
|
||||||
|
|
||||||
|
@usernames = file
|
||||||
|
end
|
||||||
|
|
||||||
def proxy=(proxy)
|
def proxy=(proxy)
|
||||||
if proxy.index(':') == nil
|
if proxy.index(':') == nil
|
||||||
raise 'Invalid proxy format. Should be host:port.'
|
raise 'Invalid proxy format. Should be host:port.'
|
||||||
@@ -136,6 +153,10 @@ class WpscanOptions
|
|||||||
!to_h.empty?
|
!to_h.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def random_agent=(useless)
|
||||||
|
@user_agent = get_random_user_agent
|
||||||
|
end
|
||||||
|
|
||||||
# return Hash
|
# return Hash
|
||||||
def to_h
|
def to_h
|
||||||
options = {}
|
options = {}
|
||||||
@@ -224,9 +245,12 @@ class WpscanOptions
|
|||||||
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
|
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--enumerate', '-e', GetoptLong::OPTIONAL_ARGUMENT],
|
['--enumerate', '-e', GetoptLong::OPTIONAL_ARGUMENT],
|
||||||
['--username', '-U', GetoptLong::REQUIRED_ARGUMENT],
|
['--username', '-U', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
|
['--usernames', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--wordlist', '-w', GetoptLong::REQUIRED_ARGUMENT],
|
['--wordlist', '-w', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--threads', '-t', GetoptLong::REQUIRED_ARGUMENT],
|
['--threads', '-t', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--force', '-f', GetoptLong::NO_ARGUMENT],
|
['--force', '-f', GetoptLong::NO_ARGUMENT],
|
||||||
|
['--user-agent', '-a', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
|
['--random-agent', '-r', GetoptLong::NO_ARGUMENT],
|
||||||
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
||||||
['--verbose', '-v', GetoptLong::NO_ARGUMENT],
|
['--verbose', '-v', GetoptLong::NO_ARGUMENT],
|
||||||
['--proxy', GetoptLong::REQUIRED_ARGUMENT],
|
['--proxy', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
@@ -239,7 +263,15 @@ class WpscanOptions
|
|||||||
['--exclude-content-based', GetoptLong::REQUIRED_ARGUMENT],
|
['--exclude-content-based', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--basic-auth', GetoptLong::REQUIRED_ARGUMENT],
|
['--basic-auth', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--debug-output', GetoptLong::NO_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],
|
||||||
|
['--log', GetoptLong::NO_ARGUMENT]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,22 @@ class CheckerPlugin < Plugin
|
|||||||
puts '[+] Checking vulnerabilities reference urls'
|
puts '[+] Checking vulnerabilities reference urls'
|
||||||
|
|
||||||
vuln_ref_files.each do |vuln_ref_file|
|
vuln_ref_files.each do |vuln_ref_file|
|
||||||
xml = xml(vuln_ref_file)
|
json = json(vuln_ref_file)
|
||||||
|
|
||||||
urls = []
|
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!
|
urls.uniq!
|
||||||
|
|
||||||
|
puts "[!] No URLs found in #{vuln_ref_file}!" if urls.empty?
|
||||||
|
|
||||||
dead_urls = []
|
dead_urls = []
|
||||||
queue_count = 0
|
queue_count = 0
|
||||||
request_count = 0
|
request_count = 0
|
||||||
@@ -73,17 +82,19 @@ class CheckerPlugin < Plugin
|
|||||||
end
|
end
|
||||||
|
|
||||||
def check_local_vulnerable_files(dir_to_scan)
|
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
|
xml_file = LOCAL_FILES_FILE
|
||||||
local_hashes = {}
|
local_hashes = {}
|
||||||
file_extension_to_scan = '*.{js,php,swf,html,htm}'
|
file_extension_to_scan = '*.{js,php,swf,html,htm}'
|
||||||
|
|
||||||
print '[+] Generating local hashes ... '
|
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
|
sha1sum = Digest::SHA1.file(filename).hexdigest
|
||||||
|
|
||||||
if local_hashes.has_key?(sha1sum)
|
if local_hashes.key?(sha1sum)
|
||||||
local_hashes[sha1sum] << filename
|
local_hashes[sha1sum] << filename
|
||||||
else
|
else
|
||||||
local_hashes[sha1sum] = [filename]
|
local_hashes[sha1sum] = [filename]
|
||||||
|
|||||||
91
lib/wpstools/plugins/checker/checker_spelling.rb
Normal file
91
lib/wpstools/plugins/checker/checker_spelling.rb
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class CheckerSpelling < Plugin
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super(author: 'WPScanTeam - @ethicalhack3r')
|
||||||
|
|
||||||
|
register_options(['--spellcheck', '--sc', 'Check all files for common spelling mistakes.'])
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(options = {})
|
||||||
|
spellcheck if options[:spellcheck]
|
||||||
|
end
|
||||||
|
|
||||||
|
def spellcheck
|
||||||
|
mistakes = 0
|
||||||
|
|
||||||
|
puts '[+] Checking for spelling mistakes'
|
||||||
|
puts
|
||||||
|
|
||||||
|
files.each do |file_name|
|
||||||
|
if File.exists?(file_name)
|
||||||
|
file = File.open(file_name, 'r')
|
||||||
|
|
||||||
|
misspellings.each_key do |misspelling|
|
||||||
|
begin
|
||||||
|
file.read.scan(/#{misspelling}/).each do |match|
|
||||||
|
mistakes += 1
|
||||||
|
puts "[MISSPELLING] File: #{file_name} Bad: #{match} Good: #{misspellings[misspelling]}"
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
puts "Error in #{file_name} #{e}"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
file.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
puts
|
||||||
|
puts "[+] Found #{mistakes} spelling mistakes"
|
||||||
|
|
||||||
|
mistakes
|
||||||
|
end
|
||||||
|
|
||||||
|
def misspellings
|
||||||
|
{
|
||||||
|
/databse/i => 'database',
|
||||||
|
/whith/i => 'with',
|
||||||
|
/wich/i => 'which',
|
||||||
|
/verions/i => 'versions',
|
||||||
|
/vulnerabilitiy/i => 'vulnerability',
|
||||||
|
/unkown/i => 'unknown',
|
||||||
|
/recieved/i => 'received',
|
||||||
|
/acheive/i => 'achieve',
|
||||||
|
/wierd/i => 'weird',
|
||||||
|
/untill/i => 'until',
|
||||||
|
/alot/i => 'a lot',
|
||||||
|
/randomstorm/ => 'RandomStorm',
|
||||||
|
/wpscan/ => 'WPScan',
|
||||||
|
/Wordpress/ => 'WordPress'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def files
|
||||||
|
files = Dir['**/*'].reject {|fn| File.directory?(fn) }
|
||||||
|
|
||||||
|
ignore.each do |ignore|
|
||||||
|
files.delete_if { |data| data.match(ignore) }
|
||||||
|
end
|
||||||
|
|
||||||
|
files
|
||||||
|
end
|
||||||
|
|
||||||
|
def ignore
|
||||||
|
ignore = []
|
||||||
|
|
||||||
|
ignore << File.basename(__FILE__)
|
||||||
|
ignore << 'spec/cache/'
|
||||||
|
ignore << 'spec/spec_session/'
|
||||||
|
ignore << 'cache/'
|
||||||
|
ignore << 'coverage/'
|
||||||
|
ignore << 'wordlist-iso-8859-1'
|
||||||
|
ignore << 'log.txt'
|
||||||
|
ignore << 'debug.log'
|
||||||
|
ignore << 'wordlist.txt'
|
||||||
|
|
||||||
|
ignore
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -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,48 +6,81 @@ class StatsPlugin < Plugin
|
|||||||
super(author: 'WPScanTeam - Christian Mehlmauer')
|
super(author: 'WPScanTeam - Christian Mehlmauer')
|
||||||
|
|
||||||
register_options(
|
register_options(
|
||||||
['--stats', '--s', 'Show WpScan Database statistics']
|
['--stats', '-s', 'Show WpScan Database statistics.']
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(options = {})
|
def run(options = {})
|
||||||
if options[:stats]
|
if options[:stats]
|
||||||
puts 'Wpscan Databse Statistics:'
|
date_wp = File.mtime(WP_VULNS_FILE)
|
||||||
puts '--------------------------'
|
date_plugins = File.mtime(PLUGINS_VULNS_FILE)
|
||||||
puts "[#] Total vulnerable versions: #{vuln_core_count}"
|
date_themes = File.mtime(THEMES_VULNS_FILE)
|
||||||
puts "[#] Total vulnerable plugins: #{vuln_plugin_count}"
|
date_plugins_full = File.mtime(PLUGINS_FULL_FILE)
|
||||||
puts "[#] Total vulnerable themes: #{vuln_theme_count}"
|
date_themes_full = File.mtime(THEMES_FULL_FILE)
|
||||||
puts "[#] Total version vulnerabilities: #{version_vulns_count}"
|
|
||||||
puts "[#] Total plugin vulnerabilities: #{plugin_vulns_count}"
|
puts "WPScan Database Statistics:"
|
||||||
puts "[#] Total theme vulnerabilities: #{theme_vulns_count}"
|
puts "---------------------------"
|
||||||
puts "[#] Total plugins to enumerate: #{total_plugins}"
|
|
||||||
puts "[#] Total themes to enumerate: #{total_themes}"
|
|
||||||
puts
|
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}"
|
||||||
|
puts
|
||||||
|
puts "[+] WordPress DB modified: #{date_wp.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
|
puts "[+] Plugins DB modified: #{date_plugins.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
|
puts "[+] Themes DB modified: #{date_themes.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
|
puts "[+] Enumeration plugins: #{date_plugins_full.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
|
puts "[+] Enumeration themes: #{date_themes_full.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
|
puts
|
||||||
|
puts "[+] Report generated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def vuln_core_count(file=WP_VULNS_FILE)
|
def vuln_core_count(file=WP_VULNS_FILE)
|
||||||
xml(file).xpath('count(//wordpress)').to_i
|
json(file).size
|
||||||
end
|
end
|
||||||
|
|
||||||
def vuln_plugin_count(file=PLUGINS_VULNS_FILE)
|
def vuln_plugin_count(file=PLUGINS_VULNS_FILE)
|
||||||
xml(file).xpath('count(//plugin)').to_i
|
json(file).size
|
||||||
end
|
end
|
||||||
|
|
||||||
def vuln_theme_count(file=THEMES_VULNS_FILE)
|
def vuln_theme_count(file=THEMES_VULNS_FILE)
|
||||||
xml(file).xpath('count(//theme)').to_i
|
json(file).size
|
||||||
end
|
end
|
||||||
|
|
||||||
def version_vulns_count(file=WP_VULNS_FILE)
|
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
|
end
|
||||||
|
|
||||||
def plugin_vulns_count(file=PLUGINS_VULNS_FILE)
|
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
|
end
|
||||||
|
|
||||||
def theme_vulns_count(file=THEMES_VULNS_FILE)
|
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
|
end
|
||||||
|
|
||||||
def total_plugins(file=PLUGINS_FULL_FILE)
|
def total_plugins(file=PLUGINS_FULL_FILE)
|
||||||
@@ -62,4 +95,12 @@ class StatsPlugin < Plugin
|
|||||||
IO.readlines(file).size
|
IO.readlines(file).size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|||||||
@@ -12,21 +12,6 @@ def usage
|
|||||||
puts
|
puts
|
||||||
puts 'Examples:'
|
puts 'Examples:'
|
||||||
puts
|
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 'Locally scan a wordpress installation for vulnerable files or shells'
|
||||||
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
|
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
|
||||||
puts
|
puts
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ describe Browser do
|
|||||||
it_behaves_like 'Browser::Actions'
|
it_behaves_like 'Browser::Actions'
|
||||||
it_behaves_like 'Browser::Options'
|
it_behaves_like 'Browser::Options'
|
||||||
|
|
||||||
CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json'
|
CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser.conf.json'
|
||||||
CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy.json'
|
CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser.conf_proxy.json'
|
||||||
#CONFIG_FILE_WITH_PROXY_AND_AUTH = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy_auth.json'
|
#CONFIG_FILE_WITH_PROXY_AND_AUTH = SPEC_FIXTURES_CONF_DIR + '/browser.conf_proxy_auth.json'
|
||||||
|
|
||||||
subject(:browser) {
|
subject(:browser) {
|
||||||
Browser.reset
|
Browser.reset
|
||||||
@@ -16,17 +16,16 @@ describe Browser do
|
|||||||
}
|
}
|
||||||
let(:options) { {} }
|
let(:options) { {} }
|
||||||
let(:instance_vars_to_check) {
|
let(:instance_vars_to_check) {
|
||||||
['user_agent', 'user_agent_mode', 'available_user_agents', 'proxy',
|
['proxy', 'max_threads', 'cache_ttl', 'request_timeout', 'connect_timeout']
|
||||||
'max_threads', 'cache_ttl', 'request_timeout', 'connect_timeout']
|
|
||||||
}
|
}
|
||||||
let(:json_config_without_proxy) { JSON.parse(File.read(CONFIG_FILE_WITHOUT_PROXY)) }
|
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)) }
|
let(:json_config_with_proxy) { JSON.parse(File.read(CONFIG_FILE_WITH_PROXY)) }
|
||||||
|
|
||||||
def check_instance_variables(browser, json_expected_vars)
|
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|
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -39,12 +38,6 @@ describe Browser do
|
|||||||
describe '::instance' do
|
describe '::instance' do
|
||||||
after { check_instance_variables(browser, @json_expected_vars) }
|
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
|
context "when :config_file = #{CONFIG_FILE_WITH_PROXY}" do
|
||||||
let(:options) { { config_file: CONFIG_FILE_WITH_PROXY } }
|
let(:options) { { config_file: CONFIG_FILE_WITH_PROXY } }
|
||||||
|
|
||||||
@@ -57,7 +50,7 @@ describe Browser do
|
|||||||
let(:cache_dir) { CACHE_DIR + '/somewhere' }
|
let(:cache_dir) { CACHE_DIR + '/somewhere' }
|
||||||
let(:options) { { cache_dir: cache_dir } }
|
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
|
it 'sets @cache_dir' do
|
||||||
@json_expected_vars = json_config_without_proxy
|
@json_expected_vars = json_config_without_proxy
|
||||||
@@ -91,11 +84,11 @@ describe Browser do
|
|||||||
|
|
||||||
describe '::append_params_header_field' do
|
describe '::append_params_header_field' do
|
||||||
after :each do
|
after :each do
|
||||||
Browser.append_params_header_field(
|
expect(Browser.append_params_header_field(
|
||||||
@params,
|
@params,
|
||||||
@field,
|
@field,
|
||||||
@field_value
|
@field_value
|
||||||
).should === @expected
|
)).to be === @expected
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is no headers' do
|
context 'when there is no headers' do
|
||||||
@@ -137,15 +130,17 @@ describe Browser do
|
|||||||
headers: { 'User-Agent' => 'SomeUA' },
|
headers: { 'User-Agent' => 'SomeUA' },
|
||||||
ssl_verifypeer: false, ssl_verifyhost: 0,
|
ssl_verifypeer: false, ssl_verifyhost: 0,
|
||||||
cookiejar: cookie_jar, cookiefile: cookie_jar,
|
cookiejar: cookie_jar, cookiefile: cookie_jar,
|
||||||
timeout: 2000, connecttimeout: 1000
|
timeout: 2000, connecttimeout: 1000,
|
||||||
|
maxredirs: 3,
|
||||||
|
referer: nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
after :each do
|
after :each do
|
||||||
browser.stub(user_agent: 'SomeUA')
|
browser.user_agent = 'SomeUA'
|
||||||
browser.cache_ttl = 250
|
browser.cache_ttl = 250
|
||||||
|
|
||||||
browser.merge_request_params(params).should == @expected
|
expect(browser.merge_request_params(params)).to eq @expected
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the User-Agent header field and cache_ttl' do
|
it 'sets the User-Agent header field and cache_ttl' do
|
||||||
@@ -187,18 +182,35 @@ describe Browser do
|
|||||||
@expected = default_expectation.merge(params)
|
@expected = default_expectation.merge(params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the maxredirs is alreday set' do
|
||||||
|
let(:params) { { maxredirs: 100 } }
|
||||||
|
|
||||||
|
it 'does not override it' 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
|
end
|
||||||
|
|
||||||
describe '#forge_request' do
|
describe '#forge_request' do
|
||||||
let(:url) { 'http://example.localhost' }
|
let(:url) { 'http://example.localhost' }
|
||||||
|
|
||||||
it 'returns the correct Typhoeus::Request' do
|
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 = subject.forge_request(url)
|
||||||
request.should be_a Typhoeus::Request
|
expect(request).to be_a Typhoeus::Request
|
||||||
request.url.should == url
|
expect(request.url).to eq url
|
||||||
request.cache_ttl.should == 10
|
expect(request.cache_ttl).to eq 10
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -213,7 +225,7 @@ describe Browser do
|
|||||||
response1 = Browser.get(url)
|
response1 = Browser.get(url)
|
||||||
response2 = 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)
|
#WebMock.should have_requested(:get, url).times(1) # This one fail, dunno why :s (but it works without mock)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,33 +17,50 @@ describe CacheFileStore do
|
|||||||
|
|
||||||
describe '#storage_path' do
|
describe '#storage_path' do
|
||||||
it 'returns the storage path given in the #new' do
|
it 'returns the storage path given in the #new' do
|
||||||
@cache.storage_path.should == cache_dir
|
expect(@cache.storage_path).to match(/#{cache_dir}/)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#serializer' do
|
describe '#serializer' do
|
||||||
it 'should return the default serializer : Marshal' do
|
it 'should return the default serializer : Marshal' do
|
||||||
@cache.serializer.should == Marshal
|
expect(@cache.serializer).to eq Marshal
|
||||||
@cache.serializer.should_not == YAML
|
expect(@cache.serializer).not_to eq YAML
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#clean' do
|
describe '#clean' do
|
||||||
it "should remove all files from the cache dir (#{@cache_dir}" do
|
it "should remove all files from the cache dir (#{@cache_dir}" do
|
||||||
# let's create some files into the directory first
|
# clean is executed by other tests before
|
||||||
(0..5).each do |i|
|
before = count_files_in_dir(@cache.cache_dir)
|
||||||
File.new(cache_dir + "/file_#{i}.txt", File::CREAT)
|
test_dir = File.expand_path("#{@cache.cache_dir}/test")
|
||||||
end
|
Dir.mkdir test_dir
|
||||||
|
#change the modification date
|
||||||
count_files_in_dir(cache_dir, 'file_*.txt').should == 6
|
%x[ touch -t 200701310846.26 #{test_dir} ]
|
||||||
|
expect(count_files_in_dir(@cache.cache_dir)).to eq (before + 1)
|
||||||
@cache.clean
|
@cache.clean
|
||||||
count_files_in_dir(cache_dir).should == 0
|
expect(count_files_in_dir(@cache.cache_dir)).to eq before
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#read_entry (nonexistent entry)' do
|
describe '#read_entry' do
|
||||||
it 'should return nil' do
|
after { expect(@cache.read_entry(key)).to eq @expected }
|
||||||
@cache.read_entry(Digest::SHA1.hexdigest('hello world')).should be_nil
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -51,7 +68,7 @@ describe CacheFileStore do
|
|||||||
|
|
||||||
after :each do
|
after :each do
|
||||||
@cache.write_entry(@key, @data, @timeout)
|
@cache.write_entry(@key, @data, @timeout)
|
||||||
@cache.read_entry(@key).should === @expected
|
expect(@cache.read_entry(@key)).to be === @expected
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should get the correct entry (string)' do
|
it 'should get the correct entry (string)' do
|
||||||
@@ -70,4 +87,16 @@ describe CacheFileStore do
|
|||||||
|
|
||||||
## TODO write / read for an object
|
## TODO write / read for an object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#storage_dir' do
|
||||||
|
it 'should create a unique storage dir' do
|
||||||
|
storage_dirs = []
|
||||||
|
|
||||||
|
(1..5).each do |i|
|
||||||
|
storage_dirs << CacheFileStore.new(cache_dir).storage_path
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(storage_dirs.uniq.size).to eq 5
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,20 +10,15 @@ describe WpItems do
|
|||||||
|
|
||||||
let(:expected) do
|
let(:expected) do
|
||||||
{
|
{
|
||||||
request_params: { cache_ttl: 0, followlocation: true },
|
request_params: { cache_ttl: 0, followlocation: true },
|
||||||
targets_items_from_file: [ WpItem.new(uri, name: 'item1'),
|
targets_items_from_file: [ WpItem.new(uri, name: 'item1'),
|
||||||
WpItem.new(uri, name:'item-2'),
|
WpItem.new(uri, name: 'item-2'),
|
||||||
WpItem.new(uri, name: 'mr-smith')],
|
WpItem.new(uri, name: 'mr-smith')],
|
||||||
|
|
||||||
vulnerable_targets_items: [ WpItem.new(uri, name: 'mr-smith'),
|
vulnerable_targets_items: [ WpItem.new(uri, name: 'mr-smith'),
|
||||||
WpItem.new(uri, name: 'neo')],
|
WpItem.new(uri, name: 'neo')],
|
||||||
|
|
||||||
passive_detection: WpItems.new << WpItem.new(uri, name: 'js-source') <<
|
passive_detection: (1..13).reduce(WpItems.new) { |o, i| o << WpItem.new(uri, name: "detect-me-#{i}") }
|
||||||
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')
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe 'WpPlugins::Detectable' do
|
|||||||
context 'when no header' do
|
context 'when no header' do
|
||||||
it 'returns an empty WpPlugins' do
|
it 'returns an empty WpPlugins' do
|
||||||
stub_request(:get, url).to_return(status: 200)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ describe 'WpPlugins::Detectable' do
|
|||||||
|
|
||||||
after :each do
|
after :each do
|
||||||
stub_request(:get, url).to_return(status: 200, headers: headers, body: '')
|
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
|
end
|
||||||
|
|
||||||
context 'when w3-total-cache detected' do
|
context 'when w3-total-cache detected' do
|
||||||
@@ -66,7 +66,7 @@ describe 'WpPlugins::Detectable' do
|
|||||||
context 'when no body' do
|
context 'when no body' do
|
||||||
it 'returns an empty WpPlugins' do
|
it 'returns an empty WpPlugins' do
|
||||||
stub_request(:get, url).to_return(status: 200, body: '')
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ describe 'WpPlugins::Detectable' do
|
|||||||
after :each do
|
after :each do
|
||||||
stub_request(:get, url).to_return(status: 200, body: @body)
|
stub_request(:get, url).to_return(status: 200, body: @body)
|
||||||
stub_request(:get, /readme\.txt/i).to_return(status: 404)
|
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
|
end
|
||||||
|
|
||||||
context 'when w3 total cache detected' do
|
context 'when w3 total cache detected' do
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ describe WpPlugins do
|
|||||||
vulnerable_targets_items: [ WpPlugin.new(uri, name: 'mr-smith'),
|
vulnerable_targets_items: [ WpPlugin.new(uri, name: 'mr-smith'),
|
||||||
WpPlugin.new(uri, name: 'neo')],
|
WpPlugin.new(uri, name: 'neo')],
|
||||||
|
|
||||||
passive_detection: WpPlugins.new << WpPlugin.new(uri, name: 'js-source') <<
|
passive_detection: WpPlugins.new << WpPlugin.new(uri, name: 'escaped-url') <<
|
||||||
WpPlugin.new(uri, name: 'escaped-url') <<
|
|
||||||
WpPlugin.new(uri, name: 'link-tag') <<
|
WpPlugin.new(uri, name: 'link-tag') <<
|
||||||
WpPlugin.new(uri, name: 'script-tag') <<
|
WpPlugin.new(uri, name: 'script-tag') <<
|
||||||
WpPlugin.new(uri, name: 'style-tag') <<
|
WpPlugin.new(uri, name: 'style-tag') <<
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe WpThemes do
|
describe WpThemes do
|
||||||
|
before { stub_request(:get, /.+\/style.css$/).to_return(status: 200) }
|
||||||
|
|
||||||
it_behaves_like 'WpItems::Detectable' do
|
it_behaves_like 'WpItems::Detectable' do
|
||||||
subject(:wp_themes) { WpThemes }
|
subject(:wp_themes) { WpThemes }
|
||||||
let(:item_class) { WpTheme }
|
let(:item_class) { WpTheme }
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ describe 'WpTimthumbs::Detectable' do
|
|||||||
|
|
||||||
describe '::passive_detection' do
|
describe '::passive_detection' do
|
||||||
it 'returns an empty WpTimthumbs' 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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ describe 'WpTimthumbs::Detectable' do
|
|||||||
after do
|
after do
|
||||||
targets = subject.send(:targets_items_from_file, file, wp_target)
|
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
|
end
|
||||||
|
|
||||||
context 'when an empty file' do
|
context 'when an empty file' do
|
||||||
@@ -71,7 +71,7 @@ describe 'WpTimthumbs::Detectable' do
|
|||||||
theme = 'hello-world'
|
theme = 'hello-world'
|
||||||
targets = subject.send(:theme_timthumbs, theme, wp_target)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ describe 'WpTimthumbs::Detectable' do
|
|||||||
after do
|
after do
|
||||||
targets = subject.send(:targets_items, wp_target, options)
|
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
|
end
|
||||||
|
|
||||||
context 'when no :theme_name' do
|
context 'when no :theme_name' do
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ describe 'WpUsers::Detectable' do
|
|||||||
|
|
||||||
describe '::request_params' do
|
describe '::request_params' do
|
||||||
it 'return an empty Hash' do
|
it 'return an empty Hash' do
|
||||||
subject.request_params.should === {}
|
expect(subject.request_params).to be === {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '::passive_detection' do
|
describe '::passive_detection' do
|
||||||
it 'return an empty WpUsers' 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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ describe 'WpUsers::Detectable' do
|
|||||||
after do
|
after do
|
||||||
targets = subject.send(:targets_items, wp_target, options)
|
targets = subject.send(:targets_items, wp_target, options)
|
||||||
|
|
||||||
targets.should == @expected
|
expect(targets).to eq @expected
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when no :range' do
|
context 'when no :range' do
|
||||||
|
|||||||
@@ -25,19 +25,19 @@ describe 'WpUsers::Output' do
|
|||||||
subject.push(@input)
|
subject.push(@input)
|
||||||
subject.flatten!
|
subject.flatten!
|
||||||
subject.remove_junk_from_display_names
|
subject.remove_junk_from_display_names
|
||||||
subject.should === @expected
|
expect(subject).to eq @expected
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return an empty array' do
|
it 'returns an empty array' do
|
||||||
@expected = @input
|
@expected = @input
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return input object' do
|
it 'returns input object' do
|
||||||
@input.push(WpUser.new(nil))
|
@input.push(WpUser.new(nil))
|
||||||
@expected = @input
|
@expected = @input
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return input object' do
|
it 'returns input object' do
|
||||||
@input.push(WpUser.new(''))
|
@input.push(WpUser.new(''))
|
||||||
@expected = @input
|
@expected = @input
|
||||||
end
|
end
|
||||||
@@ -50,23 +50,37 @@ describe 'WpUsers::Output' do
|
|||||||
@expected.push(WpUser.new('', login: '', id: 2, display_name: 'ijrjd'))
|
@expected.push(WpUser.new('', login: '', id: 2, display_name: 'ijrjd'))
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return unmodified input object' do
|
it 'returns unmodified input object' do
|
||||||
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkjh asdfa'))
|
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkjh asdfa'))
|
||||||
@input.push(WpUser.new('', login: '', id: 2, display_name: 'ijrjd asdf'))
|
@input.push(WpUser.new('', login: '', id: 2, display_name: 'ijrjd asdf'))
|
||||||
@expected = @input
|
@expected = @input
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return input object' do
|
it 'returns input object' do
|
||||||
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkjh asdf'))
|
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkjh asdf'))
|
||||||
@expected = @input
|
@expected = @input
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return an empty display_name' do
|
it 'returns an empty display_name' do
|
||||||
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkhj asdf'))
|
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkhj asdf'))
|
||||||
@input.push(WpUser.new('', login: '', id: 2, display_name: 'lkhj asdf'))
|
@input.push(WpUser.new('', login: '', id: 2, display_name: 'lkhj asdf'))
|
||||||
@expected = WpUsers.new(0)
|
@expected = WpUsers.new(0)
|
||||||
@expected.push(WpUser.new('', login: '', id: 1, display_name: ''))
|
@expected.push(WpUser.new('', login: '', id: 1, display_name: ''))
|
||||||
@expected.push(WpUser.new('', login: '', id: 2, display_name: ''))
|
@expected.push(WpUser.new('', login: '', id: 2, display_name: ''))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when a user has no display_name' do
|
||||||
|
it 'returns an empty display_name' do
|
||||||
|
@input.push(WpUser.new('', login: '', id: 1, display_name: 'lkhj asdf'))
|
||||||
|
@input.push(WpUser.new('', login: '', id: 2, display_name: 'lkhj asdf'))
|
||||||
|
@input.push(WpUser.new('', login: '', id: 3))
|
||||||
|
|
||||||
|
@expected = WpUsers.new(0)
|
||||||
|
|
||||||
|
(1..3).each do |id|
|
||||||
|
@expected.push(WpUser.new('', login: '', id: id, display_name: ''))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
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