Compare commits
196 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2014f1e4b3 | ||
|
|
4889d17e0a | ||
|
|
494d31215d | ||
|
|
582bdea431 | ||
|
|
ecf7df9c01 | ||
|
|
a9760e8817 | ||
|
|
b32e990dd4 | ||
|
|
4320d2436f | ||
|
|
cba6e74b13 | ||
|
|
981bcf5fa2 | ||
|
|
1d79bc37d3 | ||
|
|
2fae3336ba | ||
|
|
cfb98c5139 | ||
|
|
b0260327c4 | ||
|
|
f65532e347 | ||
|
|
ff574b046c | ||
|
|
97c995b64c | ||
|
|
8361ec97e4 | ||
|
|
7a0bbc0acb | ||
|
|
66f5eca841 | ||
|
|
b53e6d1888 | ||
|
|
4b68fa8b60 | ||
|
|
54770c5a50 | ||
|
|
39fb2167f7 | ||
|
|
c33fef9c98 | ||
|
|
08a1117edf | ||
|
|
e14cbed56e | ||
|
|
56e2ab16cc | ||
|
|
d76d4b70f5 | ||
|
|
e223936a81 | ||
|
|
60d067c421 | ||
|
|
4102cf4688 | ||
|
|
dc977e6630 | ||
|
|
05deabd775 | ||
|
|
549ab4aa15 | ||
|
|
b189c71682 | ||
|
|
b909856933 | ||
|
|
5de9084901 | ||
|
|
384ef0b44c | ||
|
|
9307772dc3 | ||
|
|
730c71d103 | ||
|
|
5c710b96f5 | ||
|
|
fe63d0eadf | ||
|
|
a6ca95159a | ||
|
|
677d32fef5 | ||
|
|
14abd05969 | ||
|
|
2e680be34f | ||
|
|
fe29942bf4 | ||
|
|
c8fb717ac1 | ||
|
|
1ff7fcc913 | ||
|
|
419c32702a | ||
|
|
9b63714caa | ||
|
|
f034233607 | ||
|
|
be6fcb51b6 | ||
|
|
e49a682f00 | ||
|
|
23ad3141a1 | ||
|
|
5347e374e0 | ||
|
|
1a49a628de | ||
|
|
8def256d7e | ||
|
|
1cd8e6bad7 | ||
|
|
7a03c0db25 | ||
|
|
e7e3657d1f | ||
|
|
734dfcc9bc | ||
|
|
b0db15099d | ||
|
|
6fbd2369ba | ||
|
|
f4a6674eed | ||
|
|
c0567ad4f5 | ||
|
|
f146ee7e9f | ||
|
|
e606f4ce18 | ||
|
|
945b589a58 | ||
|
|
b18042c4a8 | ||
|
|
a9ff39104b | ||
|
|
f6af6e5880 | ||
|
|
57c6c2d471 | ||
|
|
c362527903 | ||
|
|
a7acbd0738 | ||
|
|
f67192ebce | ||
|
|
c44fde83e4 | ||
|
|
50119285ef | ||
|
|
6216916fed | ||
|
|
2952380200 | ||
|
|
fb42b82e0d | ||
|
|
6d381ab88d | ||
|
|
c5c1de32bc | ||
|
|
8077ad9bcd | ||
|
|
6f22ba350f | ||
|
|
f23d0c0157 | ||
|
|
a9a38edf24 | ||
|
|
a5534f1e49 | ||
|
|
1c6469f384 | ||
|
|
8cfdbc1196 | ||
|
|
88737ca6ea | ||
|
|
45bebc60bd | ||
|
|
4f7dec4635 | ||
|
|
98739cce5a | ||
|
|
0bfbfacc27 | ||
|
|
73cd862e83 | ||
|
|
3305e9b74f | ||
|
|
c37ec0e8d0 | ||
|
|
0b005477c1 | ||
|
|
a1467f8dac | ||
|
|
40d2c34347 | ||
|
|
528270e767 | ||
|
|
f4a04b2387 | ||
|
|
14ed6ae109 | ||
|
|
4fd43694ae | ||
|
|
552d731e6a | ||
|
|
49ac3ef528 | ||
|
|
4379313f12 | ||
|
|
3901949f36 | ||
|
|
a3d8593fed | ||
|
|
7c5baeb9c7 | ||
|
|
c692db5f85 | ||
|
|
9130196ffc | ||
|
|
dad4a65118 | ||
|
|
4c34c2feb7 | ||
|
|
23522f7775 | ||
|
|
82c61398ba | ||
|
|
02871050a6 | ||
|
|
7d3b1fea6b | ||
|
|
24917fa2a6 | ||
|
|
de3d8e4a23 | ||
|
|
1502845d65 | ||
|
|
af3f10f74e | ||
|
|
c100372b31 | ||
|
|
72d699b39a | ||
|
|
7d2b8a2a8b | ||
|
|
8729c68e22 | ||
|
|
e2d48bedd9 | ||
|
|
6b241ce9b3 | ||
|
|
1b68bdb36c | ||
|
|
fb82538441 | ||
|
|
2709d0869a | ||
|
|
343f87bbe7 | ||
|
|
ecbfc6004c | ||
|
|
c57eecc81b | ||
|
|
7ea14dc03f | ||
|
|
4340d27258 | ||
|
|
e911be8f14 | ||
|
|
a4c650cdff | ||
|
|
31a58f8a8f | ||
|
|
ba4f15f111 | ||
|
|
206a913eb9 | ||
|
|
21ba490073 | ||
|
|
2a29e2ed95 | ||
|
|
9517d14fd3 | ||
|
|
3deaa896df | ||
|
|
c117007dc0 | ||
|
|
50baa238b9 | ||
|
|
0e2d771660 | ||
|
|
32b4670755 | ||
|
|
4a032d5e12 | ||
|
|
5887fede15 | ||
|
|
ad4eeb9f81 | ||
|
|
a62c16d7cc | ||
|
|
e766e7392a | ||
|
|
025c9c24ca | ||
|
|
ab052add27 | ||
|
|
15cb99977b | ||
|
|
82d5af926f | ||
|
|
76f73f3dc8 | ||
|
|
575b22320e | ||
|
|
d20c07dc85 | ||
|
|
f89071b87a | ||
|
|
8b4e90f285 | ||
|
|
9c4f57c786 | ||
|
|
902ec24b77 | ||
|
|
7eba77fa63 | ||
|
|
0753bbf7b3 | ||
|
|
6b2333614a | ||
|
|
80b7f458f5 | ||
|
|
dbd8e59cf4 | ||
|
|
9948230ea0 | ||
|
|
e2c858ac69 | ||
|
|
bac8b613e6 | ||
|
|
abbae15c6f | ||
|
|
1548e8bfc1 | ||
|
|
dc8cf3fc34 | ||
|
|
c3cd815567 | ||
|
|
ce543b9384 | ||
|
|
9755c8cf42 | ||
|
|
434a210fb5 | ||
|
|
587602665a | ||
|
|
bfec63df41 | ||
|
|
3b150df1af | ||
|
|
f24ecf0537 | ||
|
|
9ddecbcc0a | ||
|
|
947bb8d3d5 | ||
|
|
30cbf87b35 | ||
|
|
69c3aab35a | ||
|
|
bdeb3547f1 | ||
|
|
99e04b9669 | ||
|
|
680d2fb7eb | ||
|
|
8814eda018 | ||
|
|
7e72ba2885 | ||
|
|
b4d7a8490b |
@@ -14,3 +14,4 @@ Dockerfile
|
||||
*.orig
|
||||
bin/wpscan-*
|
||||
.wpscan/
|
||||
.github/
|
||||
|
||||
41
.github/workflows/build.yml
vendored
Normal file
41
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
ruby: [2.5, 2.6, 2.7]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Set up Ruby ${{ matrix.ruby }}
|
||||
uses: actions/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
|
||||
- name: Install GEMs
|
||||
run: |
|
||||
gem install bundler
|
||||
bundle config force_ruby_platform true
|
||||
bundle config path vendor/bundle
|
||||
bundle install --jobs 4 --retry 3
|
||||
|
||||
- name: rubocop
|
||||
run: |
|
||||
bundle exec rubocop
|
||||
|
||||
- name: rspec
|
||||
run: |
|
||||
bundle exec rspec
|
||||
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
40
.github/workflows/gempush.yml
vendored
Normal file
40
.github/workflows/gempush.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Ruby Gem
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build + Publish
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Set up Ruby 2.6
|
||||
uses: actions/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6.x
|
||||
|
||||
#- name: Publish to GPR
|
||||
# run: |
|
||||
# mkdir -p $HOME/.gem
|
||||
# touch $HOME/.gem/credentials
|
||||
# chmod 0600 $HOME/.gem/credentials
|
||||
# printf -- "---\n:github: Bearer ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||
# gem build *.gemspec
|
||||
# gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
||||
# env:
|
||||
# GEM_HOST_API_KEY: ${{secrets.GITHUB_TOKEN}}
|
||||
# OWNER: wpscanteam
|
||||
|
||||
- name: Publish to RubyGems
|
||||
run: |
|
||||
mkdir -p $HOME/.gem
|
||||
touch $HOME/.gem/credentials
|
||||
chmod 0600 $HOME/.gem/credentials
|
||||
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||
gem build *.gemspec
|
||||
gem push *.gem
|
||||
env:
|
||||
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
||||
@@ -1,9 +1,12 @@
|
||||
require: rubocop-performance
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.4
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 2.5
|
||||
Exclude:
|
||||
- '*.gemspec'
|
||||
- 'vendor/**/*'
|
||||
Layout/LineLength:
|
||||
Max: 120
|
||||
Lint/UriEscapeUnescape:
|
||||
Enabled: false
|
||||
Metrics/AbcSize:
|
||||
@@ -16,9 +19,7 @@ Metrics/ClassLength:
|
||||
Exclude:
|
||||
- 'app/controllers/enumeration/cli_options.rb'
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 8
|
||||
Metrics/LineLength:
|
||||
Max: 120
|
||||
Max: 10
|
||||
Metrics/MethodLength:
|
||||
Max: 20
|
||||
Exclude:
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.6.2
|
||||
2.7.1
|
||||
|
||||
15
.simplecov
15
.simplecov
@@ -1,4 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if ENV['GITHUB_ACTION']
|
||||
require 'simplecov-lcov'
|
||||
|
||||
SimpleCov::Formatter::LcovFormatter.config do |c|
|
||||
c.single_report_path = 'coverage/lcov.info'
|
||||
c.report_with_single_file = true
|
||||
end
|
||||
|
||||
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
|
||||
end
|
||||
|
||||
SimpleCov.start do
|
||||
enable_coverage :branch # Only supported for Ruby >= 2.5
|
||||
|
||||
add_filter '/spec/'
|
||||
add_filter 'helper'
|
||||
end
|
||||
16
.travis.yml
16
.travis.yml
@@ -1,16 +0,0 @@
|
||||
language: ruby
|
||||
sudo: false
|
||||
cache: bundler
|
||||
rvm:
|
||||
- 2.4.9
|
||||
- 2.5.7
|
||||
- 2.6.5
|
||||
before_install:
|
||||
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
|
||||
- gem update --system
|
||||
script:
|
||||
- bundle exec rubocop
|
||||
- bundle exec rspec
|
||||
notifications:
|
||||
email:
|
||||
- team@wpscan.org
|
||||
16
Dockerfile
16
Dockerfile
@@ -1,16 +1,16 @@
|
||||
FROM ruby:2.6.3-alpine AS builder
|
||||
FROM ruby:2.7.1-alpine AS builder
|
||||
LABEL maintainer="WPScan Team <team@wpscan.org>"
|
||||
|
||||
ARG BUNDLER_ARGS="--jobs=8 --without test development"
|
||||
|
||||
RUN echo "gem: --no-ri --no-rdoc" > /etc/gemrc
|
||||
RUN echo "install: --no-document --no-post-install-message\nupdate: --no-document --no-post-install-message" > /etc/gemrc
|
||||
|
||||
COPY . /wpscan
|
||||
|
||||
RUN apk add --no-cache git libcurl ruby-dev libffi-dev make gcc musl-dev zlib-dev procps sqlite-dev && \
|
||||
bundle install --system --clean --no-cache --gemfile=/wpscan/Gemfile $BUNDLER_ARGS && \
|
||||
# temp fix for https://github.com/bundler/bundler/issues/6680
|
||||
rm -rf /usr/local/bundle/cache
|
||||
bundle config force_ruby_platform true && \
|
||||
bundle config disable_version_check 'true' && \
|
||||
bundle config without "test development" && \
|
||||
bundle config path.system 'true' && \
|
||||
bundle install --gemfile=/wpscan/Gemfile --jobs=8
|
||||
|
||||
WORKDIR /wpscan
|
||||
RUN rake install --trace
|
||||
@@ -19,7 +19,7 @@ RUN rake install --trace
|
||||
RUN chmod -R a+r /usr/local/bundle
|
||||
|
||||
|
||||
FROM ruby:2.6.3-alpine
|
||||
FROM ruby:2.7.1-alpine
|
||||
LABEL maintainer="WPScan Team <team@wpscan.org>"
|
||||
|
||||
RUN adduser -h /wpscan -g WPScan -D wpscan
|
||||
|
||||
31
README.md
31
README.md
@@ -7,15 +7,15 @@
|
||||
<h3 align="center">WPScan</h3>
|
||||
|
||||
<p align="center">
|
||||
WordPress Vulnerability Scanner
|
||||
WordPress Security Scanner
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://wpscan.org/" title="homepage" target="_blank">Homepage</a> - <a href="https://wpscan.io/" title="wpscan.io" target="_blank">WPScan.io</a> - <a href="https://wpvulndb.com/" title="vulnerability database" target="_blank">Vulnerability Database</a> - <a href="https://wordpress.org/plugins/wpscan/" title="wordpress plugin" target="_blank">WordPress Plugin</a>
|
||||
<a href="https://wpscan.org/" title="homepage" target="_blank">Homepage</a> - <a href="https://wpscan.io/" title="wpscan.io" target="_blank">WPScan.io</a> - <a href="https://wpvulndb.com/" title="vulnerability database" target="_blank">Vulnerability Database</a> - <a href="https://wordpress.org/plugins/wpscan/" title="wordpress security plugin" target="_blank">WordPress Security Plugin</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://badge.fury.io/rb/wpscan" target="_blank"><img src="https://badge.fury.io/rb/wpscan.svg"></a>
|
||||
<a href="https://travis-ci.org/wpscanteam/wpscan" target="_blank"><img src="https://travis-ci.org/wpscanteam/wpscan.svg?branch=master"></a>
|
||||
<a href="https://github.com/wpscanteam/wpscan/actions?query=workflow%3ABuild" target="_blank"><img src="https://github.com/wpscanteam/wpscan/workflows/Build/badge.svg"></a>
|
||||
<a href="https://codeclimate.com/github/wpscanteam/wpscan" target="_blank"><img src="https://codeclimate.com/github/wpscanteam/wpscan/badges/gpa.svg"></a>
|
||||
</p>
|
||||
|
||||
@@ -31,7 +31,11 @@
|
||||
- RubyGems - Recommended: latest
|
||||
- Nokogiri might require packages to be installed via your package manager depending on your OS, see https://nokogiri.org/tutorials/installing_nokogiri.html
|
||||
|
||||
### From RubyGems (Recommended)
|
||||
### In a Pentesting distribution
|
||||
|
||||
When using a pentesting distubution (such as Kali Linux), it is recommended to install/update wpscan via the package manager if available.
|
||||
|
||||
### From RubyGems
|
||||
|
||||
```shell
|
||||
gem install wpscan
|
||||
@@ -39,18 +43,6 @@ gem install wpscan
|
||||
|
||||
On MacOSX, if a ```Gem::FilePermissionError``` is raised due to the Apple's System Integrity Protection (SIP), either install RVM and install wpscan again, or run ```sudo gem install -n /usr/local/bin wpscan``` (see [#1286](https://github.com/wpscanteam/wpscan/issues/1286))
|
||||
|
||||
### From sources (NOT Recommended)
|
||||
|
||||
Prerequisites: Git
|
||||
|
||||
```shell
|
||||
git clone https://github.com/wpscanteam/wpscan
|
||||
|
||||
cd wpscan/
|
||||
|
||||
bundle install && rake install
|
||||
```
|
||||
|
||||
# Updating
|
||||
|
||||
You can update the local database by using ```wpscan --update```
|
||||
@@ -77,6 +69,8 @@ docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-1
|
||||
|
||||
# Usage
|
||||
|
||||
Full user documentation can be found here; https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation
|
||||
|
||||
```wpscan --url blog.tld``` This will scan the blog using default options with a good compromise between speed and accuracy. For example, the plugins will be checked passively but their version with a mixed detection mode (passively + aggressively). Potential config backup files will also be checked, along with other interesting findings.
|
||||
|
||||
If a more stealthy approach is required, then ```wpscan --stealthy --url blog.tld``` can be used.
|
||||
@@ -130,6 +124,11 @@ cli_options:
|
||||
api_token: YOUR_API_TOKEN
|
||||
```
|
||||
|
||||
## Load API Token From ENV (since v3.7.10)
|
||||
|
||||
The API Token will be automatically loaded from the ENV variable `WPSCAN_API_TOKEN` if present. If the `--api-token` CLI option is also provided, the value from the CLI will be used.
|
||||
|
||||
|
||||
## Enumerating usernames
|
||||
|
||||
```shell
|
||||
|
||||
@@ -18,9 +18,7 @@ module WPScan
|
||||
target.content_dir = ParsedCli.wp_content_dir if ParsedCli.wp_content_dir
|
||||
target.plugins_dir = ParsedCli.wp_plugins_dir if ParsedCli.wp_plugins_dir
|
||||
|
||||
return if target.content_dir
|
||||
|
||||
raise Error::WpContentDirNotDetected
|
||||
raise Error::WpContentDirNotDetected unless target.content_dir
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,7 +51,7 @@ module WPScan
|
||||
OptSmartList.new(['--plugins-list LIST', 'List of plugins to enumerate'], advanced: true),
|
||||
OptChoice.new(
|
||||
['--plugins-detection MODE',
|
||||
'Use the supplied mode to enumerate Plugins, instead of the global (--detection-mode) mode.'],
|
||||
'Use the supplied mode to enumerate Plugins.'],
|
||||
choices: %w[mixed passive aggressive], normalize: :to_sym, default: :passive
|
||||
),
|
||||
OptBoolean.new(
|
||||
@@ -62,8 +62,7 @@ module WPScan
|
||||
),
|
||||
OptChoice.new(
|
||||
['--plugins-version-detection MODE',
|
||||
'Use the supplied mode to check plugins versions instead of the --detection-mode ' \
|
||||
'or --plugins-detection modes.'],
|
||||
'Use the supplied mode to check plugins\' versions.'],
|
||||
choices: %w[mixed passive aggressive], normalize: :to_sym, default: :mixed
|
||||
),
|
||||
OptInteger.new(
|
||||
|
||||
@@ -23,27 +23,32 @@ module WPScan
|
||||
]
|
||||
end
|
||||
|
||||
def attack_opts
|
||||
@attack_opts ||= {
|
||||
show_progression: user_interaction?,
|
||||
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
||||
}
|
||||
end
|
||||
|
||||
def run
|
||||
return unless ParsedCli.passwords
|
||||
|
||||
begin
|
||||
found = []
|
||||
|
||||
if user_interaction?
|
||||
output('@info',
|
||||
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
||||
end
|
||||
|
||||
attack_opts = {
|
||||
show_progression: user_interaction?,
|
||||
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
||||
}
|
||||
|
||||
begin
|
||||
found = []
|
||||
|
||||
attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user|
|
||||
attacker.attack(users, ParsedCli.passwords, attack_opts) do |user|
|
||||
found << user
|
||||
|
||||
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
|
||||
end
|
||||
rescue Error::NoLoginInterfaceDetected => e
|
||||
# TODO: Maybe output that in JSON as well.
|
||||
output('@notice', msg: e.to_s) if user_interaction?
|
||||
ensure
|
||||
output('users', users: found)
|
||||
end
|
||||
@@ -65,6 +70,8 @@ module WPScan
|
||||
|
||||
case ParsedCli.password_attack
|
||||
when :wp_login
|
||||
raise Error::NoLoginInterfaceDetected unless target.login_url
|
||||
|
||||
Finders::Passwords::WpLogin.new(target)
|
||||
when :xmlrpc
|
||||
raise Error::XMLRPCNotDetected unless xmlrpc
|
||||
@@ -81,8 +88,8 @@ module WPScan
|
||||
def xmlrpc_get_users_blogs_enabled?
|
||||
if xmlrpc&.enabled? &&
|
||||
xmlrpc.available_methods.include?('wp.getUsersBlogs') &&
|
||||
xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
|
||||
.run.body !~ /XML\-RPC services are disabled/
|
||||
!xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
|
||||
.run.body.match?(/>\s*405\s*</)
|
||||
|
||||
true
|
||||
else
|
||||
@@ -100,8 +107,10 @@ module WPScan
|
||||
else
|
||||
Finders::Passwords::XMLRPC.new(xmlrpc)
|
||||
end
|
||||
else
|
||||
elsif target.login_url
|
||||
Finders::Passwords::WpLogin.new(target)
|
||||
else
|
||||
raise Error::NoLoginInterfaceDetected
|
||||
end
|
||||
end
|
||||
|
||||
@@ -113,15 +122,6 @@ module WPScan
|
||||
acc << Model::User.new(elem.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
# @param [ String ] wordlist_path
|
||||
#
|
||||
# @return [ Array<String> ]
|
||||
def passwords(wordlist_path)
|
||||
@passwords ||= File.open(wordlist_path).reduce([]) do |acc, elem|
|
||||
acc << elem.chomp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,8 @@ module WPScan
|
||||
module Controller
|
||||
# Controller to handle the API token
|
||||
class VulnApi < CMSScanner::Controller::Base
|
||||
ENV_KEY = 'WPSCAN_API_TOKEN'
|
||||
|
||||
def cli_options
|
||||
[
|
||||
OptString.new(['--api-token TOKEN', 'The WPVulnDB API Token to display vulnerability data'])
|
||||
@@ -11,9 +13,9 @@ module WPScan
|
||||
end
|
||||
|
||||
def before_scan
|
||||
return unless ParsedCli.api_token
|
||||
return unless ParsedCli.api_token || ENV.key?(ENV_KEY)
|
||||
|
||||
DB::VulnApi.token = ParsedCli.api_token
|
||||
DB::VulnApi.token = ParsedCli.api_token || ENV[ENV_KEY]
|
||||
|
||||
api_status = DB::VulnApi.status
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
def potential_urls(opts = {})
|
||||
urls = {}
|
||||
domain_name = PublicSuffix.domain(target.uri.host)[/(^[\w|-]+)/, 1]
|
||||
domain_name = (PublicSuffix.domain(target.uri.host) || target.uri.host)[/(^[\w|-]+)/, 1]
|
||||
|
||||
File.open(opts[:list]).each_with_index do |path, index|
|
||||
path.gsub!('{domain_name}', domain_name)
|
||||
|
||||
@@ -16,8 +16,7 @@ module WPScan
|
||||
target.url(path),
|
||||
confidence: 70,
|
||||
found_by: DIRECT_ACCESS,
|
||||
interesting_entries: target.directory_listing_entries(path),
|
||||
references: { url: 'https://github.com/wpscanteam/wpscan/issues/422' }
|
||||
interesting_entries: target.directory_listing_entries(path)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,11 +11,7 @@ module WPScan
|
||||
|
||||
return unless target.debug_log?(path)
|
||||
|
||||
Model::DebugLog.new(
|
||||
target.url(path),
|
||||
confidence: 100, found_by: DIRECT_ACCESS,
|
||||
references: { url: 'https://codex.wordpress.org/Debugging_in_WordPress' }
|
||||
)
|
||||
Model::DebugLog.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,12 +11,7 @@ module WPScan
|
||||
|
||||
return unless /DUPLICATOR INSTALL-LOG/.match?(target.head_and_get(path).body)
|
||||
|
||||
Model::DuplicatorInstallerLog.new(
|
||||
target.url(path),
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
references: { url: 'https://www.exploit-db.com/ghdb/3981/' }
|
||||
)
|
||||
Model::DuplicatorInstallerLog.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,10 +15,7 @@ module WPScan
|
||||
Model::EmergencyPwdResetScript.new(
|
||||
target.url(path),
|
||||
confidence: /password/i.match?(res.body) ? 100 : 40,
|
||||
found_by: DIRECT_ACCESS,
|
||||
references: {
|
||||
url: 'https://codex.wordpress.org/Resetting_Your_Password#Using_the_Emergency_Password_Reset_Script'
|
||||
}
|
||||
found_by: DIRECT_ACCESS
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,8 +16,7 @@ module WPScan
|
||||
target.url(path),
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
interesting_entries: fpd_entries,
|
||||
references: { url: 'https://www.owasp.org/index.php/Full_Path_Disclosure' }
|
||||
interesting_entries: fpd_entries
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,22 +7,16 @@ module WPScan
|
||||
class MuPlugins < CMSScanner::Finders::Finder
|
||||
# @return [ InterestingFinding ]
|
||||
def passive(_opts = {})
|
||||
pattern = %r{#{target.content_dir}/mu\-plugins/}i
|
||||
pattern = %r{#{target.content_dir}/mu-plugins/}i
|
||||
|
||||
target.in_scope_uris(target.homepage_res) do |uri|
|
||||
target.in_scope_uris(target.homepage_res, '(//@href|//@src)[contains(., "mu-plugins")]') do |uri|
|
||||
next unless uri.path&.match?(pattern)
|
||||
|
||||
url = target.url('wp-content/mu-plugins/')
|
||||
|
||||
target.mu_plugins = true
|
||||
|
||||
return Model::MuPlugins.new(
|
||||
url,
|
||||
confidence: 70,
|
||||
found_by: 'URLs In Homepage (Passive Detection)',
|
||||
to_s: "This site has 'Must Use Plugins': #{url}",
|
||||
references: { url: 'http://codex.wordpress.org/Must_Use_Plugins' }
|
||||
)
|
||||
return Model::MuPlugins.new(url, confidence: 70, found_by: 'URLs In Homepage (Passive Detection)')
|
||||
end
|
||||
nil
|
||||
end
|
||||
@@ -37,13 +31,7 @@ module WPScan
|
||||
|
||||
target.mu_plugins = true
|
||||
|
||||
Model::MuPlugins.new(
|
||||
url,
|
||||
confidence: 80,
|
||||
found_by: DIRECT_ACCESS,
|
||||
to_s: "This site has 'Must Use Plugins': #{url}",
|
||||
references: { url: 'http://codex.wordpress.org/Must_Use_Plugins' }
|
||||
)
|
||||
Model::MuPlugins.new(url, confidence: 80, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,18 +12,12 @@ module WPScan
|
||||
location = res.headers_hash['location']
|
||||
|
||||
return unless [200, 302].include?(res.code)
|
||||
return if res.code == 302 && location =~ /wp-login\.php\?action=register/
|
||||
return unless res.code == 200 || res.code == 302 && location =~ /wp-signup\.php/
|
||||
return if res.code == 302 && location&.include?('wp-login.php?action=register')
|
||||
return unless res.code == 200 || res.code == 302 && location&.include?('wp-signup.php')
|
||||
|
||||
target.multisite = true
|
||||
|
||||
Model::Multisite.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
to_s: 'This site seems to be a multisite',
|
||||
references: { url: 'http://codex.wordpress.org/Glossary#Multisite' }
|
||||
)
|
||||
Model::Multisite.new(url, confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,12 +20,7 @@ module WPScan
|
||||
|
||||
target.registration_enabled = true
|
||||
|
||||
Model::Registration.new(
|
||||
res.effective_url,
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
to_s: "Registration is enabled: #{res.effective_url}"
|
||||
)
|
||||
Model::Registration.new(res.effective_url, confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,12 +13,7 @@ module WPScan
|
||||
|
||||
return unless res.code == 200 && res.headers['Content-Type'] =~ %r{\Aapplication/zip}i
|
||||
|
||||
Model::TmmDbMigrate.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
references: { packetstorm: 131_957 }
|
||||
)
|
||||
Model::TmmDbMigrate.new(url, confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,12 +13,7 @@ module WPScan
|
||||
|
||||
url = target.url(path)
|
||||
|
||||
Model::UploadDirectoryListing.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
to_s: "Upload directory has listing enabled: #{url}"
|
||||
)
|
||||
Model::UploadDirectoryListing.new(url, confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,11 +14,7 @@ module WPScan
|
||||
|
||||
return unless SQL_PATTERN.match?(res.body)
|
||||
|
||||
Model::UploadSQLDump.new(
|
||||
target.url(path),
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS
|
||||
)
|
||||
Model::UploadSQLDump.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,17 +11,7 @@ module WPScan
|
||||
|
||||
return unless res.code == 200
|
||||
|
||||
Model::WPCron.new(
|
||||
wp_cron_url,
|
||||
confidence: 60,
|
||||
found_by: DIRECT_ACCESS,
|
||||
references: {
|
||||
url: [
|
||||
'https://www.iplocation.net/defend-wordpress-from-ddos',
|
||||
'https://github.com/wpscanteam/wpscan/issues/1299'
|
||||
]
|
||||
}
|
||||
)
|
||||
Model::WPCron.new(wp_cron_url, confidence: 60, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
|
||||
def wp_cron_url
|
||||
|
||||
@@ -20,8 +20,8 @@ module WPScan
|
||||
end
|
||||
|
||||
def passive_from_css_href(res, opts)
|
||||
target.in_scope_uris(res, '//style/@src|//link/@href') do |uri|
|
||||
next unless uri.path =~ %r{/themes/([^\/]+)/style.css\z}i
|
||||
target.in_scope_uris(res, '//link/@href[contains(., "style.css")]') do |uri|
|
||||
next unless uri.path =~ %r{/themes/([^/]+)/style.css\z}i
|
||||
|
||||
return create_theme(Regexp.last_match[1], uri.to_s, opts)
|
||||
end
|
||||
@@ -33,7 +33,7 @@ module WPScan
|
||||
code = tag.text.to_s
|
||||
next if code.empty?
|
||||
|
||||
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'\( ]*}i
|
||||
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'( ]*}i
|
||||
|
||||
return create_theme(Regexp.last_match[1], Regexp.last_match[0].strip, opts)
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ module WPScan
|
||||
|
||||
def valid_credentials?(response)
|
||||
response.code == 302 &&
|
||||
[*response.headers['Set-Cookie']]&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
|
||||
Array(response.headers['Set-Cookie'])&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
|
||||
end
|
||||
|
||||
def errored_response?(response)
|
||||
|
||||
@@ -12,11 +12,11 @@ module WPScan
|
||||
end
|
||||
|
||||
def valid_credentials?(response)
|
||||
response.code == 200 && response.body =~ /blogName/
|
||||
response.code == 200 && response.body.include?('blogName')
|
||||
end
|
||||
|
||||
def errored_response?(response)
|
||||
response.code != 200 && response.body !~ /login_error/i
|
||||
response.code != 200 && response.body !~ /Incorrect username or password/i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,8 +22,30 @@ module WPScan
|
||||
target.multi_call(methods, cache_ttl: 0).run
|
||||
end
|
||||
|
||||
# @param [ IO ] file
|
||||
# @param [ Integer ] passwords_size
|
||||
# @return [ Array<String> ] The passwords from the last checked position in the file until there are
|
||||
# passwords_size passwords retrieved
|
||||
def passwords_from_wordlist(file, passwords_size)
|
||||
pwds = []
|
||||
added_pwds = 0
|
||||
|
||||
return pwds if passwords_size.zero?
|
||||
|
||||
# Make sure that the main code does not call #sysseek or #count etc
|
||||
# otherwise the file descriptor will be set to somwehere else
|
||||
file.each_line(chomp: true) do |line|
|
||||
pwds << line
|
||||
added_pwds += 1
|
||||
|
||||
break if added_pwds == passwords_size
|
||||
end
|
||||
|
||||
pwds
|
||||
end
|
||||
|
||||
# @param [ Array<Model::User> ] users
|
||||
# @param [ Array<String> ] passwords
|
||||
# @param [ String ] wordlist_path
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ Boolean ] :show_progression
|
||||
# @option opts [ Integer ] :multicall_max_passwords
|
||||
@@ -33,18 +55,22 @@ module WPScan
|
||||
# TODO: Make rubocop happy about metrics etc
|
||||
#
|
||||
# rubocop:disable all
|
||||
def attack(users, passwords, opts = {})
|
||||
wordlist_index = 0
|
||||
def attack(users, wordlist_path, opts = {})
|
||||
checked_passwords = 0
|
||||
wordlist = File.open(wordlist_path)
|
||||
wordlist_size = wordlist.count
|
||||
max_passwords = opts[:multicall_max_passwords]
|
||||
current_passwords_size = passwords_size(max_passwords, users.size)
|
||||
|
||||
create_progress_bar(total: (passwords.size / current_passwords_size.round(1)).ceil,
|
||||
create_progress_bar(total: (wordlist_size / current_passwords_size.round(1)).ceil,
|
||||
show_progression: opts[:show_progression])
|
||||
|
||||
wordlist.sysseek(0) # reset the descriptor to the beginning of the file as it changed with #count
|
||||
|
||||
loop do
|
||||
current_users = users.select { |user| user.password.nil? }
|
||||
current_passwords = passwords[wordlist_index, current_passwords_size]
|
||||
wordlist_index += current_passwords_size
|
||||
current_passwords = passwords_from_wordlist(wordlist, current_passwords_size)
|
||||
checked_passwords += current_passwords_size
|
||||
|
||||
break if current_users.empty? || current_passwords.nil? || current_passwords.empty?
|
||||
|
||||
@@ -76,16 +102,19 @@ module WPScan
|
||||
break
|
||||
end
|
||||
|
||||
progress_bar.total = progress_bar.progress + ((passwords.size - wordlist_index) / current_passwords_size.round(1)).ceil
|
||||
begin
|
||||
progress_bar.total = progress_bar.progress + ((wordlist_size - checked_passwords) / current_passwords_size.round(1)).ceil
|
||||
rescue ProgressBar::InvalidProgressError
|
||||
end
|
||||
end
|
||||
end
|
||||
# Maybe a progress_bar.stop ?
|
||||
end
|
||||
# rubocop:disable all
|
||||
# rubocop:enable all
|
||||
|
||||
def passwords_size(max_passwords, users_size)
|
||||
return 1 if max_passwords < users_size
|
||||
return 0 if users_size == 0
|
||||
return 0 if users_size.zero?
|
||||
|
||||
max_passwords / users_size
|
||||
end
|
||||
@@ -94,9 +123,13 @@ module WPScan
|
||||
def check_and_output_errors(res)
|
||||
progress_bar.log("Incorrect response: #{res.code} / #{res.return_message}") unless res.code == 200
|
||||
|
||||
progress_bar.log('Parsing error, might be caused by a too high --max-passwords value (such as >= 2k)') if res.body =~ /parse error. not well formed/i
|
||||
if /parse error. not well formed/i.match?(res.body)
|
||||
progress_bar.log('Parsing error, might be caused by a too high --max-passwords value (such as >= 2k)')
|
||||
end
|
||||
|
||||
progress_bar.log('The requested method is not supported') if res.body =~ /requested method [^ ]+ does not exist/i
|
||||
return unless /requested method [^ ]+ does not exist/i.match?(res.body)
|
||||
|
||||
progress_bar.log('The requested method is not supported')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String, nil ] The version number detected from the stable tag
|
||||
def from_stable_tag(body)
|
||||
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z\.-]+)/i
|
||||
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z.-]+)/i
|
||||
|
||||
number = Regexp.last_match[1]
|
||||
|
||||
@@ -59,7 +59,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String, nil ] The best version number detected from the changelog section
|
||||
def from_changelog_section(body)
|
||||
extracted_versions = body.scan(%r{[=]+\s+(?:v(?:ersion)?\s*)?([0-9\.-]+)[ \ta-z0-9\(\)\.\-\/]*[=]+}i)
|
||||
extracted_versions = body.scan(%r{=+\s+(?:v(?:ersion)?\s*)?([0-9.-]+)[ \ta-z0-9().\-/]*=+}i)
|
||||
|
||||
return if extracted_versions.nil? || extracted_versions.empty?
|
||||
|
||||
@@ -68,12 +68,10 @@ module WPScan
|
||||
extracted_versions = extracted_versions.select { |x| x =~ /[0-9]+/ }
|
||||
|
||||
sorted = extracted_versions.sort do |x, y|
|
||||
begin
|
||||
Gem::Version.new(x) <=> Gem::Version.new(y)
|
||||
rescue StandardError
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
sorted.last
|
||||
end
|
||||
|
||||
@@ -19,8 +19,12 @@ module WPScan
|
||||
def aggressive(opts = {})
|
||||
found = []
|
||||
|
||||
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |_res, slug|
|
||||
found << Model::Plugin.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
|
||||
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
|
||||
finding_opts = opts.merge(found_by: found_by,
|
||||
confidence: 80,
|
||||
interesting_entries: ["#{res.effective_url}, status: #{res.code}"])
|
||||
|
||||
found << Model::Plugin.new(slug, target, finding_opts)
|
||||
|
||||
raise Error::PluginsThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ module WPScan
|
||||
|
||||
# @return [ Version ]
|
||||
def style_version
|
||||
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z\.-]+)/i
|
||||
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z.-]+)/i
|
||||
|
||||
Model::Version.new(
|
||||
Regexp.last_match[1],
|
||||
|
||||
@@ -19,8 +19,12 @@ module WPScan
|
||||
def aggressive(opts = {})
|
||||
found = []
|
||||
|
||||
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |_res, slug|
|
||||
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
|
||||
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
|
||||
finding_opts = opts.merge(found_by: found_by,
|
||||
confidence: 80,
|
||||
interesting_entries: ["#{res.effective_url}, status: #{res.code}"])
|
||||
|
||||
found << Model::Theme.new(slug, target, finding_opts)
|
||||
|
||||
raise Error::ThemesThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ require_relative 'users/oembed_api'
|
||||
require_relative 'users/rss_generator'
|
||||
require_relative 'users/author_id_brute_forcing'
|
||||
require_relative 'users/login_error_messages'
|
||||
require_relative 'users/yoast_seo_author_sitemap.rb'
|
||||
require_relative 'users/yoast_seo_author_sitemap'
|
||||
|
||||
module WPScan
|
||||
module Finders
|
||||
|
||||
@@ -71,11 +71,13 @@ module WPScan
|
||||
return username, 'Display Name', 50 if username
|
||||
end
|
||||
|
||||
# @param [ String ] url
|
||||
# @param [ String, Addressable::URI ] uri
|
||||
#
|
||||
# @return [ String, nil ]
|
||||
def username_from_author_url(url)
|
||||
url[%r{/author/([^/\b]+)/?}i, 1]
|
||||
def username_from_author_url(uri)
|
||||
uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
|
||||
|
||||
uri.path[%r{/author/([^/\b]+)/?}i, 1]
|
||||
end
|
||||
|
||||
# @param [ Typhoeus::Response ] res
|
||||
@@ -83,12 +85,12 @@ module WPScan
|
||||
# @return [ String, nil ] The username found
|
||||
def username_from_response(res)
|
||||
# Permalink enabled
|
||||
target.in_scope_uris(res, '//link/@href|//a/@href') do |uri|
|
||||
username = username_from_author_url(uri.to_s)
|
||||
target.in_scope_uris(res, '//@href[contains(., "author/")]') do |uri|
|
||||
username = username_from_author_url(uri)
|
||||
return username if username
|
||||
end
|
||||
|
||||
# No permalink
|
||||
# No permalink, TODO Maybe use xpath to extract the classes ?
|
||||
res.body[/<body class="archive author author-([^\s]+)[ "]/i, 1]
|
||||
end
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ module WPScan
|
||||
def potential_usernames(res)
|
||||
usernames = []
|
||||
|
||||
target.in_scope_uris(res, '//a/@href') do |uri, node|
|
||||
target.in_scope_uris(res, '//a/@href[contains(., "author")]') do |uri, node|
|
||||
if uri.path =~ %r{/author/([^/\b]+)/?\z}i
|
||||
usernames << [Regexp.last_match[1], 'Author Pattern', 100]
|
||||
elsif /author=[0-9]+/.match?(uri.query)
|
||||
|
||||
@@ -37,7 +37,7 @@ module WPScan
|
||||
# usernames from the potential Users found
|
||||
unames = opts[:found].map(&:username)
|
||||
|
||||
[*opts[:list]].each { |uname| unames << uname.chomp }
|
||||
Array(opts[:list]).each { |uname| unames << uname.chomp }
|
||||
|
||||
unames.uniq
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ module WPScan
|
||||
urls.each do |url|
|
||||
res = Browser.get_and_follow_location(url)
|
||||
|
||||
next unless res.code == 200 && res.body =~ /<dc\:creator>/i
|
||||
next unless res.code == 200 && res.body =~ /<dc:creator>/i
|
||||
|
||||
potential_usernames = []
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ module WPScan
|
||||
loop do
|
||||
current_page += 1
|
||||
|
||||
res = Typhoeus.get(api_url, params: { per_page: MAX_PER_PAGE, page: current_page })
|
||||
res = Browser.get(api_url, params: { per_page: MAX_PER_PAGE, page: current_page })
|
||||
|
||||
total_pages ||= res.headers['X-WP-TotalPages'].to_i
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ module WPScan
|
||||
found = []
|
||||
|
||||
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
|
||||
username = user_tag.text.to_s[%r{/author/([^\/]+)/}, 1]
|
||||
username = user_tag.text.to_s[%r{/author/([^/]+)/}, 1]
|
||||
|
||||
next unless username && !username.strip.empty?
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@ module WPScan
|
||||
# @param [ String ] type plugins / themes
|
||||
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
|
||||
#
|
||||
# @return [Array<String> ] The plugins/themes detected in the href, src attributes of the homepage
|
||||
# @return [ Array<String> ] The plugins/themes detected in the href, src attributes of the page
|
||||
def items_from_links(type, uniq = true)
|
||||
found = []
|
||||
xpath = format(
|
||||
'(//@href|//@src|//@data-src)[contains(., "%s")]',
|
||||
type == 'plugins' ? target.plugins_dir : target.content_dir
|
||||
)
|
||||
|
||||
target.in_scope_uris(page_res) do |uri|
|
||||
target.in_scope_uris(page_res, xpath) do |uri|
|
||||
next unless uri.to_s =~ item_attribute_pattern(type)
|
||||
|
||||
slug = Regexp.last_match[1]&.strip
|
||||
@@ -51,7 +55,7 @@ module WPScan
|
||||
#
|
||||
# @return [ Regexp ]
|
||||
def item_code_pattern(type)
|
||||
@item_code_pattern ||= %r{["'\( ]#{item_url_pattern(type)}([^\\\/\)"']+)}i
|
||||
@item_code_pattern ||= %r{["'( ]#{item_url_pattern(type)}([^\\/)"']+)}i
|
||||
end
|
||||
|
||||
# @param [ String ] type
|
||||
@@ -62,9 +66,9 @@ module WPScan
|
||||
item_url = type == 'plugins' ? target.plugins_url : target.content_url
|
||||
|
||||
url = /#{item_url.gsub(/\A(?:https?)/i, 'https?').gsub('/', '\\\\\?\/')}/i
|
||||
item_dir = %r{(?:#{url}|\\?\/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
|
||||
item_dir = %r{(?:#{url}|\\?/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
|
||||
|
||||
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?\/}i
|
||||
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?/}i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ module WPScan
|
||||
end
|
||||
|
||||
def passive_urls_xpath
|
||||
'//a[contains(@href, "rdf")]/@href'
|
||||
'//a[contains(@href, "/rdf")]/@href'
|
||||
end
|
||||
|
||||
def aggressive_urls(_opts = {})
|
||||
|
||||
@@ -7,46 +7,130 @@ module WPScan
|
||||
include References
|
||||
end
|
||||
|
||||
#
|
||||
# Empty classes for the #type to be correctly displayed (as taken from the self.class from the parent)
|
||||
#
|
||||
class BackupDB < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "A backup directory has been found: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://github.com/wpscanteam/wpscan/issues/422'] }
|
||||
end
|
||||
end
|
||||
|
||||
class DebugLog < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "Debug Log found: #{url}"
|
||||
end
|
||||
|
||||
# @ return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://codex.wordpress.org/Debugging_in_WordPress'] }
|
||||
end
|
||||
end
|
||||
|
||||
class DuplicatorInstallerLog < InterestingFinding
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://www.exploit-db.com/ghdb/3981/'] }
|
||||
end
|
||||
end
|
||||
|
||||
class EmergencyPwdResetScript < InterestingFinding
|
||||
def references
|
||||
@references ||= {
|
||||
url: ['https://codex.wordpress.org/Resetting_Your_Password#Using_the_Emergency_Password_Reset_Script']
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
class FullPathDisclosure < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "Full Path Disclosure found: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://www.owasp.org/index.php/Full_Path_Disclosure'] }
|
||||
end
|
||||
end
|
||||
|
||||
class MuPlugins < InterestingFinding
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
@to_s ||= "This site has 'Must Use Plugins': #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['http://codex.wordpress.org/Must_Use_Plugins'] }
|
||||
end
|
||||
end
|
||||
|
||||
class Multisite < InterestingFinding
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
@to_s ||= 'This site seems to be a multisite'
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['http://codex.wordpress.org/Glossary#Multisite'] }
|
||||
end
|
||||
end
|
||||
|
||||
class Readme < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "WordPress readme found: #{url}"
|
||||
end
|
||||
end
|
||||
|
||||
class Registration < InterestingFinding
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
@to_s ||= "Registration is enabled: #{url}"
|
||||
end
|
||||
end
|
||||
|
||||
class TmmDbMigrate < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "ThemeMakers migration file found: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { packetstorm: [131_957] }
|
||||
end
|
||||
end
|
||||
|
||||
class UploadDirectoryListing < InterestingFinding
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
@to_s ||= "Upload directory has listing enabled: #{url}"
|
||||
end
|
||||
end
|
||||
|
||||
class UploadSQLDump < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "SQL Dump found: #{url}"
|
||||
end
|
||||
end
|
||||
|
||||
class WPCron < InterestingFinding
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
@to_s ||= "The external WP-Cron seems to be enabled: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= {
|
||||
url: [
|
||||
'https://www.iplocation.net/defend-wordpress-from-ddos',
|
||||
'https://github.com/wpscanteam/wpscan/issues/1299'
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,7 +38,7 @@ module WPScan
|
||||
|
||||
# @return [ Array<String> ]
|
||||
def potential_readme_filenames
|
||||
@potential_readme_filenames ||= [*(DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super)]
|
||||
@potential_readme_filenames ||= Array((DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,7 +45,7 @@ module WPScan
|
||||
# @return [ Theme ]
|
||||
def parent_theme
|
||||
return unless template
|
||||
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
|
||||
return unless style_body =~ /^@import\surl\(["']?([^"')]+)["']?\);\s*$/i
|
||||
|
||||
opts = detection_opts.merge(
|
||||
style_url: url(Regexp.last_match[1]),
|
||||
@@ -101,7 +101,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String ]
|
||||
def parse_style_tag(body, tag)
|
||||
value = body[/#{Regexp.escape(tag)}:[\t ]*([^\r\n\*]+)/i, 1]
|
||||
value = body[/\b#{Regexp.escape(tag)}:[\t ]*([^\r\n*]+)/, 1]
|
||||
|
||||
value && !value.strip.empty? ? value.strip : nil
|
||||
end
|
||||
|
||||
@@ -40,9 +40,9 @@ module WPScan
|
||||
def rce_132_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 1.32 Remote Code Execution',
|
||||
{ exploitdb: ['17602'] },
|
||||
'RCE',
|
||||
'1.33'
|
||||
references: { exploitdb: ['17602'] },
|
||||
type: 'RCE',
|
||||
fixed_in: '1.33'
|
||||
)
|
||||
end
|
||||
|
||||
@@ -50,12 +50,12 @@ module WPScan
|
||||
def rce_webshot_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
|
||||
{
|
||||
references: {
|
||||
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
|
||||
cve: '2014-4663'
|
||||
},
|
||||
'RCE',
|
||||
'2.8.14'
|
||||
type: 'RCE',
|
||||
fixed_in: '2.8.14'
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ module WPScan
|
||||
# @option opts [ Hash ] :version_detection The options to use when looking for the version
|
||||
# @option opts [ String ] :url The URL of the item
|
||||
def initialize(slug, blog, opts = {})
|
||||
@slug = URI.decode(slug)
|
||||
@slug = Addressable::URI.unencode(slug)
|
||||
@blog = blog
|
||||
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
|
||||
|
||||
@@ -39,7 +39,7 @@ module WPScan
|
||||
|
||||
@vulnerabilities = []
|
||||
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
Array(db_data['vulnerabilities']).each do |json_vuln|
|
||||
vulnerability = Vulnerability.load_from_json(json_vuln)
|
||||
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
||||
end
|
||||
@@ -83,11 +83,6 @@ module WPScan
|
||||
end
|
||||
end
|
||||
|
||||
# URI.encode is preferered over Addressable::URI.encode as it will encode
|
||||
# leading # character:
|
||||
# URI.encode('#t#') => %23t%23
|
||||
# Addressable::URI.encode('#t#') => #t%23
|
||||
#
|
||||
# @param [ String ] path Optional path to merge with the uri
|
||||
#
|
||||
# @return [ String ]
|
||||
@@ -95,7 +90,7 @@ module WPScan
|
||||
return unless @uri
|
||||
return @uri.to_s unless path
|
||||
|
||||
@uri.join(URI.encode(path)).to_s
|
||||
@uri.join(Addressable::URI.encode(path)).to_s
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
@@ -166,7 +161,7 @@ module WPScan
|
||||
# @return [ Typhoeus::Response ]
|
||||
def head_and_get(path, codes = [200], params = {})
|
||||
final_path = +@path_from_blog
|
||||
final_path << URI.encode(path) unless path.nil?
|
||||
final_path << path unless path.nil?
|
||||
|
||||
blog.head_and_get(final_path, codes, params)
|
||||
end
|
||||
|
||||
@@ -53,7 +53,7 @@ module WPScan
|
||||
|
||||
@vulnerabilities = []
|
||||
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
Array(db_data['vulnerabilities']).each do |json_vuln|
|
||||
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
|
||||
end
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ module WPScan
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
{
|
||||
@references ||= {
|
||||
url: ['http://codex.wordpress.org/XML-RPC_Pingback_API'],
|
||||
metasploit: [
|
||||
'auxiliary/scanner/http/wordpress_ghost_scanner',
|
||||
|
||||
@@ -9,6 +9,6 @@ _______________________________________________________________
|
||||
WordPress Security Scanner by the WPScan Team
|
||||
Version <%= WPScan::VERSION %>
|
||||
<%= ' ' * ((63 - WPScan::DB::Sponsor.text.length)/2) + WPScan::DB::Sponsor.text %>
|
||||
@_WPScan_, @ethicalhack3r, @erwan_lr, @_FireFart_
|
||||
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
|
||||
_______________________________________________________________
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<% if @users.empty? -%>
|
||||
<%= notice_icon %> No Valid Passwords Found.
|
||||
<% else -%>
|
||||
<%= notice_icon %> Valid Combinations Found:
|
||||
<%= critical_icon %> Valid Combinations Found:
|
||||
<% @users.each do |user| -%>
|
||||
| Username: <%= user.username %>, Password: <%= user.password %>
|
||||
<% end -%>
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
<% end -%>
|
||||
<% else -%>
|
||||
<%= warning_icon %> No WPVulnDB API Token given, as a result vulnerability data has not been output.
|
||||
<%= warning_icon %> You can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up.
|
||||
<%= warning_icon %> You can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up
|
||||
<% end -%>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
| <%= critical_icon %> Title: <%= @v.title %>
|
||||
<% if @v.cvss -%>
|
||||
| CVSS: <%= @v.cvss[:score] %> (<%= @v.cvss[:vector] %>)
|
||||
<% end -%>
|
||||
<% if @v.fixed_in -%>
|
||||
| Fixed in: <%= @v.fixed_in %>
|
||||
<% end -%>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"@_WPScan_",
|
||||
"@ethicalhack3r",
|
||||
"@erwan_lr",
|
||||
"@_FireFart_"
|
||||
"@firefart"
|
||||
],
|
||||
"sponsor": <%= WPScan::DB::Sponsor.text.to_json %>
|
||||
},
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<% vulns.each_with_index do |v, index| -%>
|
||||
{
|
||||
"title": <%= v.title.to_json %>,
|
||||
<% if v.cvss -%>
|
||||
"cvss": <%= v.cvss.to_json %>,
|
||||
<% end -%>
|
||||
"fixed_in": <%= v.fixed_in.to_json %>,
|
||||
"references": <%= v.references.to_json %>
|
||||
}<% unless index == last_index -%>,<% end -%>
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"requests_remaining": <%= @status['requests_remaining'].to_json %>
|
||||
<% end -%>
|
||||
<% else -%>
|
||||
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up."
|
||||
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up"
|
||||
<% end -%>
|
||||
},
|
||||
@@ -67,13 +67,13 @@ module WPScan
|
||||
# @return [ Hash ] The params for Typhoeus::Request
|
||||
# @note Those params can't be overriden by CLI options
|
||||
def request_params
|
||||
@request_params ||= {
|
||||
@request_params ||= Browser.instance.default_connect_request_params.merge(
|
||||
timeout: 600,
|
||||
connecttimeout: 300,
|
||||
accept_encoding: 'gzip, deflate',
|
||||
cache_ttl: 0,
|
||||
headers: { 'User-Agent' => Browser.instance.default_user_agent, 'Referer' => nil }
|
||||
}
|
||||
headers: { 'User-Agent' => Browser.instance.default_user_agent }
|
||||
)
|
||||
end
|
||||
|
||||
# @return [ String ] The raw file URL associated with the given filename
|
||||
@@ -85,7 +85,7 @@ module WPScan
|
||||
def remote_file_checksum(filename)
|
||||
url = "#{remote_file_url(filename)}.sha512"
|
||||
|
||||
res = Browser.get(url, request_params)
|
||||
res = Typhoeus.get(url, request_params)
|
||||
raise Error::Download, res if res.timed_out? || res.code != 200
|
||||
|
||||
res.body.chomp
|
||||
@@ -126,7 +126,7 @@ module WPScan
|
||||
file_path = local_file_path(filename)
|
||||
file_url = remote_file_url(filename)
|
||||
|
||||
res = Browser.get(file_url, request_params)
|
||||
res = Typhoeus.get(file_url, request_params)
|
||||
raise Error::Download, res if res.timed_out? || res.code != 200
|
||||
|
||||
File.open(file_path, 'wb') { |f| f.write(res.body) }
|
||||
@@ -139,7 +139,6 @@ module WPScan
|
||||
updated = []
|
||||
|
||||
FILES.each do |filename|
|
||||
begin
|
||||
db_checksum = remote_file_checksum(filename)
|
||||
|
||||
# Checking if the file needs to be updated
|
||||
@@ -148,7 +147,7 @@ module WPScan
|
||||
create_backup(filename)
|
||||
dl_checksum = download(filename)
|
||||
|
||||
raise "#{filename}: checksums do not match" unless dl_checksum == db_checksum
|
||||
raise Error::ChecksumsMismatch, filename unless dl_checksum == db_checksum
|
||||
|
||||
updated << filename
|
||||
rescue StandardError => e
|
||||
@@ -157,7 +156,6 @@ module WPScan
|
||||
ensure
|
||||
delete_backup(filename) if File.exist?(backup_file_path(filename))
|
||||
end
|
||||
end
|
||||
|
||||
File.write(last_update_file, Time.now)
|
||||
|
||||
|
||||
@@ -21,8 +21,10 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
def self.get(path, params = {})
|
||||
return {} unless token
|
||||
return {} if path.end_with?('/latest') # Remove this when api/v4 is up
|
||||
|
||||
res = Browser.get(uri.join(path), params.merge(request_params))
|
||||
# Typhoeus.get is used rather than Browser.get to avoid merging irrelevant params from the CLI
|
||||
res = Typhoeus.get(uri.join(path), default_request_params.merge(params))
|
||||
|
||||
return {} if res.code == 404 # This is for API inconsistencies when dots in path
|
||||
return JSON.parse(res.body) if NON_ERROR_CODES.include?(res.code)
|
||||
@@ -64,15 +66,14 @@ module WPScan
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def self.request_params
|
||||
{
|
||||
# @note Those params can not be overriden by CLI options
|
||||
def self.default_request_params
|
||||
Browser.instance.default_connect_request_params.merge(
|
||||
headers: {
|
||||
'Host' => uri.host, # Reset in case user provided a --vhost for the target
|
||||
'Referer' => nil, # Removes referer set by the cmsscanner to the target url
|
||||
'User-Agent' => Browser.instance.default_user_agent,
|
||||
'Authorization' => "Token token=#{token}"
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,5 +8,17 @@ module WPScan
|
||||
'Update required, you can not run a scan if a database file is missing.'
|
||||
end
|
||||
end
|
||||
|
||||
class ChecksumsMismatch < Standard
|
||||
attr_reader :db_file
|
||||
|
||||
def initialize(db_file)
|
||||
@db_file = db_file
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{db_file}: checksums do not match. Please try again in a few minutes."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,5 +29,11 @@ module WPScan
|
||||
' use the --scope option or make sure the --url value given is the correct one'
|
||||
end
|
||||
end
|
||||
|
||||
class NoLoginInterfaceDetected < Standard
|
||||
def to_s
|
||||
'Could not find a login interface to perform the password attack against'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,7 +17,7 @@ module WPScan
|
||||
end
|
||||
|
||||
# Needed to have inheritance of the @child_class_constants
|
||||
# If inheritance is not needed, then the #child_class_constant can be used in the classe definition, ie
|
||||
# If inheritance is not needed, then the #child_class_constant can be used in the class definition, ie
|
||||
# child_class_constant :FILES, PATTERN: /aaa/i
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
|
||||
@@ -11,7 +11,7 @@ module WPScan
|
||||
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super.merge(
|
||||
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 70
|
||||
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[.\d]+)/, CONFIDENCE: 70
|
||||
)
|
||||
end
|
||||
|
||||
@@ -21,14 +21,12 @@ module WPScan
|
||||
parsers = ALLOWED_PARSERS.include?(self.class::PARSER) ? [self.class::PARSER] : ALLOWED_PARSERS
|
||||
|
||||
parsers.each do |parser|
|
||||
begin
|
||||
parsed = parser.respond_to?(:safe_load) ? parser.safe_load(body) : parser.load(body)
|
||||
|
||||
return parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
|
||||
rescue StandardError
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
nil # Make sure nil is returned in case none of the parsers managed to parse the body correctly
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super().merge(
|
||||
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)\=(?<v>\d+\.[\.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
|
||||
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)=(?<v>\d+\.[.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super().merge(
|
||||
XPATH: nil, PATTERN: /\A(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 60
|
||||
XPATH: nil, PATTERN: /\A(?<v>\d+\.[.\d]+)/, CONFIDENCE: 60
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -33,10 +33,12 @@ module WPScan
|
||||
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super().merge(PATTERN: /ver\=(?<v>\d+\.[\.\d]+)/i)
|
||||
@child_class_constants ||= super().merge(PATTERN: /ver=(?<v>\d+\.[.\d]+)/i)
|
||||
end
|
||||
end
|
||||
|
||||
# This one has been disabled from the DF.yml as it was causing FPs when a plugin had numerous
|
||||
# files matching a known WP version.
|
||||
class WpItemQueryParameter < QueryParameter
|
||||
def xpath
|
||||
@xpath ||=
|
||||
|
||||
@@ -13,7 +13,7 @@ end
|
||||
#
|
||||
# @return [ Symbol ]
|
||||
def classify_slug(slug)
|
||||
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/\-{1,}/, '_').camelize.to_s
|
||||
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/-{1,}/, '_').camelize.to_s
|
||||
classified = "D_#{classified}" if /\d/.match?(classified[0])
|
||||
|
||||
classified.to_sym
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
module WPScan
|
||||
# References module (which should be included along with the CMSScanner::References)
|
||||
# to allow the use of the wpvulndb reference
|
||||
# to allow the use of the wpvulndb reference.
|
||||
module References
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ module WPScan
|
||||
# @return [ Boolean ]
|
||||
def vulnerable?
|
||||
[@wp_version, @main_theme, @plugins, @themes, @timthumbs].each do |e|
|
||||
[*e].each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
|
||||
Array(e).each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
|
||||
end
|
||||
|
||||
return true unless [*@config_backups].empty?
|
||||
return true unless [*@db_exports].empty?
|
||||
return true unless Array(@config_backups).empty?
|
||||
return true unless Array(@db_exports).empty?
|
||||
|
||||
[*@users].each { |u| return true if u.password }
|
||||
Array(@users).each { |u| return true if u.password }
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
@@ -11,9 +11,9 @@ module WPScan
|
||||
module WordPress
|
||||
include CMSScanner::Target::Platform::PHP
|
||||
|
||||
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i.freeze
|
||||
WP_JSON_OEMBED_PATTERN = %r{/wp\-json/oembed/}i.freeze
|
||||
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp\-admin\\?/admin\-ajax\.php}i.freeze
|
||||
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu-)?plugins|uploads))|wp-includes)/}i.freeze
|
||||
WP_JSON_OEMBED_PATTERN = %r{/wp-json/oembed/}i.freeze
|
||||
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp-admin\\?/admin-ajax\.php}i.freeze
|
||||
|
||||
# These methods are used in the associated interesting_findings finders
|
||||
# to keep the boolean state of the finding rather than re-check the whole thing again
|
||||
@@ -32,8 +32,12 @@ module WPScan
|
||||
|
||||
if %i[mixed aggressive].include?(detection_mode)
|
||||
%w[wp-admin/install.php wp-login.php].each do |path|
|
||||
return true if in_scope_uris(Browser.get_and_follow_location(url(path))).any? do |uri|
|
||||
WORDPRESS_PATTERN.match?(uri.path)
|
||||
res = Browser.get_and_follow_location(url(path))
|
||||
|
||||
next unless res.code == 200
|
||||
|
||||
in_scope_uris(res, '//link/@href|//script/@src') do |uri|
|
||||
return true if WORDPRESS_PATTERN.match?(uri.path)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -44,7 +48,7 @@ module WPScan
|
||||
# @param [ Typhoeus::Response ] response
|
||||
# @return [ Boolean ]
|
||||
def wordpress_from_meta_comments_or_scripts?(response)
|
||||
in_scope_uris(response) do |uri|
|
||||
in_scope_uris(response, '//link/@href|//script/@src') do |uri|
|
||||
return true if WORDPRESS_PATTERN.match?(uri.path) || WP_JSON_OEMBED_PATTERN.match?(uri.path)
|
||||
end
|
||||
|
||||
@@ -100,8 +104,9 @@ module WPScan
|
||||
|
||||
unless content_dir
|
||||
pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
|
||||
xpath = '(//@href|//@src)[contains(., "wp.com")]'
|
||||
|
||||
uris_from_page(homepage_res) do |uri|
|
||||
uris_from_page(homepage_res, xpath) do |uri|
|
||||
return true if uri.to_s.match?(pattern)
|
||||
end
|
||||
end
|
||||
@@ -134,15 +139,16 @@ module WPScan
|
||||
# the first time the method is called, and the effective_url is then used
|
||||
# if suitable, otherwise the default wp-login will be.
|
||||
#
|
||||
# @return [ String ] The URL to the login page
|
||||
# @return [ String, false ] The URL to the login page or false if not detected
|
||||
def login_url
|
||||
return @login_url if @login_url
|
||||
return @login_url unless @login_url.nil?
|
||||
|
||||
@login_url = url('wp-login.php')
|
||||
@login_url = url('wp-login.php') # TODO: url(ParsedCli.login_uri)
|
||||
|
||||
res = Browser.get_and_follow_location(@login_url)
|
||||
|
||||
@login_url = res.effective_url if res.effective_url =~ /wp\-login\.php\z/i && in_scope?(res.effective_url)
|
||||
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
|
||||
@login_url = false if res.code == 404
|
||||
|
||||
@login_url
|
||||
end
|
||||
|
||||
@@ -71,7 +71,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String ]
|
||||
def plugin_url(slug)
|
||||
plugins_uri.join("#{URI.encode(slug)}/").to_s
|
||||
plugins_uri.join("#{Addressable::URI.encode(slug)}/").to_s
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
@@ -93,7 +93,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String ]
|
||||
def theme_url(slug)
|
||||
themes_uri.join("#{URI.encode(slug)}/").to_s
|
||||
themes_uri.join("#{Addressable::URI.encode(slug)}/").to_s
|
||||
end
|
||||
|
||||
# @return [ String, False ] String of the sub_dir found, false otherwise
|
||||
@@ -104,10 +104,11 @@ module WPScan
|
||||
return @sub_dir unless @sub_dir.nil?
|
||||
|
||||
# url_pattern is from CMSScanner::Target
|
||||
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp\-includes/)}i
|
||||
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp-includes/)}i
|
||||
xpath = '(//@src|//@href|//@data-src)[contains(., "xmlrpc.php") or contains(., "wp-includes/")]'
|
||||
|
||||
[homepage_res, error_404_res].each do |page_res|
|
||||
in_scope_uris(page_res) do |uri|
|
||||
in_scope_uris(page_res, xpath) do |uri|
|
||||
return @sub_dir = Regexp.last_match[1] if uri.to_s.match(pattern)
|
||||
end
|
||||
end
|
||||
@@ -123,9 +124,9 @@ module WPScan
|
||||
def url(path = nil)
|
||||
return @uri.to_s unless path
|
||||
|
||||
if %r{wp\-content/plugins}i.match?(path)
|
||||
if %r{wp-content/plugins}i.match?(path)
|
||||
path = +path.gsub('wp-content/plugins', plugins_dir)
|
||||
elsif /wp\-content/i.match?(path)
|
||||
elsif /wp-content/i.match?(path)
|
||||
path = +path.gsub('wp-content', content_dir)
|
||||
elsif path[0] != '/' && sub_dir
|
||||
path = "#{sub_dir}/#{path}"
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
# Version
|
||||
module WPScan
|
||||
VERSION = '3.7.5'
|
||||
VERSION = '3.8.5'
|
||||
end
|
||||
|
||||
@@ -18,9 +18,10 @@ module WPScan
|
||||
|
||||
new(
|
||||
json_data['title'],
|
||||
references,
|
||||
json_data['vuln_type'],
|
||||
json_data['fixed_in']
|
||||
references: references,
|
||||
type: json_data['vuln_type'],
|
||||
fixed_in: json_data['fixed_in'],
|
||||
cvss: json_data['cvss']&.symbolize_keys
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
XMLRPC_FAILED_BODY = '
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodResponse>
|
||||
<fault>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>faultCode</name>
|
||||
<value><int>405</int></value>
|
||||
</member>
|
||||
<member>
|
||||
<name>faultString</name>
|
||||
<value><string>%s</string></value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</fault>
|
||||
</methodResponse>'
|
||||
|
||||
describe WPScan::Controller::PasswordAttack do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
@@ -21,7 +40,7 @@ describe WPScan::Controller::PasswordAttack do
|
||||
|
||||
describe '#users' do
|
||||
context 'when no --usernames' do
|
||||
it 'calles target.users' do
|
||||
it 'calls target.users' do
|
||||
expect(controller.target).to receive(:users)
|
||||
controller.users
|
||||
end
|
||||
@@ -40,10 +59,6 @@ describe WPScan::Controller::PasswordAttack do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#passwords' do
|
||||
xit
|
||||
end
|
||||
|
||||
describe '#run' do
|
||||
context 'when no --passwords is supplied' do
|
||||
it 'does not run the attacker' do
|
||||
@@ -85,20 +100,34 @@ describe WPScan::Controller::PasswordAttack do
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method listed' do
|
||||
before { expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2]) }
|
||||
before do
|
||||
expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2])
|
||||
|
||||
stub_request(:post, xmlrpc.url).to_return(body: body)
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method disabled' do
|
||||
it 'returns false' do
|
||||
stub_request(:post, xmlrpc.url).to_return(body: 'XML-RPC services are disabled on this site.')
|
||||
context 'when blog is in EN' do
|
||||
let(:body) { format(XMLRPC_FAILED_BODY, 'XML-RPC services are disabled on this site.') }
|
||||
|
||||
it 'returns false' do
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method enabled' do
|
||||
it 'returns true' do
|
||||
stub_request(:post, xmlrpc.url).to_return(body: 'Incorrect username or password.')
|
||||
context 'when blog is in FR' do
|
||||
let(:body) { format(XMLRPC_FAILED_BODY, 'Les services XML-RPC sont désactivés sur ce site.') }
|
||||
|
||||
it 'returns false' do
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method enabled' do
|
||||
let(:body) { 'Incorrect username or password.' }
|
||||
|
||||
it 'returns true' do
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be true
|
||||
end
|
||||
end
|
||||
@@ -107,18 +136,37 @@ describe WPScan::Controller::PasswordAttack do
|
||||
end
|
||||
|
||||
describe '#attacker' do
|
||||
before do
|
||||
allow(controller.target).to receive(:sub_dir)
|
||||
controller.target.instance_variable_set(:@login_url, nil)
|
||||
end
|
||||
|
||||
context 'when --password-attack provided' do
|
||||
let(:cli_args) { "#{super()} --password-attack #{attack}" }
|
||||
|
||||
context 'when wp-login' do
|
||||
before { stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status) }
|
||||
|
||||
let(:attack) { 'wp-login' }
|
||||
|
||||
context 'when available' do
|
||||
let(:status) { 200 }
|
||||
|
||||
it 'returns the correct object' do
|
||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||
expect(controller.attacker.target).to be_a WPScan::Target
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not available (404)' do
|
||||
let(:status) { 404 }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when xmlrpc' do
|
||||
context 'when xmlrpc not detected on target' do
|
||||
before do
|
||||
@@ -172,14 +220,29 @@ describe WPScan::Controller::PasswordAttack do
|
||||
|
||||
context 'when automatic detection' do
|
||||
context 'when xmlrpc_get_users_blogs_enabled? is false' do
|
||||
it 'returns the WpLogin' do
|
||||
before do
|
||||
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false)
|
||||
stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status)
|
||||
end
|
||||
|
||||
context 'when wp-login available' do
|
||||
let(:status) { 200 }
|
||||
|
||||
it 'returns the WpLogin' do
|
||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||
expect(controller.attacker.target).to be_a WPScan::Target
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wp-login.php not available' do
|
||||
let(:status) { 404 }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when xmlrpc_get_users_blogs_enabled? is true' do
|
||||
before do
|
||||
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(true)
|
||||
|
||||
@@ -74,20 +74,40 @@ describe WPScan::Controller::VulnApi do
|
||||
context 'when limited requests' do
|
||||
let(:requests) { 100 }
|
||||
|
||||
it 'does not raise an error' do
|
||||
it 'sets the token and does not raise an error' do
|
||||
expect { controller.before_scan }.to_not raise_error
|
||||
|
||||
expect(WPScan::DB::VulnApi.token).to eql 'token'
|
||||
end
|
||||
|
||||
context 'when unlimited requests' do
|
||||
let(:requests) { 'Unlimited' }
|
||||
|
||||
it 'does not raise an error' do
|
||||
it 'sets the token and does not raise an error' do
|
||||
expect { controller.before_scan }.to_not raise_error
|
||||
|
||||
expect(WPScan::DB::VulnApi.token).to eql 'token'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when token in ENV' do
|
||||
before do
|
||||
ENV[described_class::ENV_KEY] = 'token-from-env'
|
||||
|
||||
expect(WPScan::DB::VulnApi)
|
||||
.to receive(:status)
|
||||
.and_return('success' => true, 'plan' => 'free', 'requests_remaining' => 'Unlimited')
|
||||
end
|
||||
|
||||
it 'sets the token and does not raise an error' do
|
||||
expect { controller.before_scan }.to_not raise_error
|
||||
|
||||
expect(WPScan::DB::VulnApi.token).to eql 'token-from-env'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
allow(target).to receive(:sub_dir).and_return(false)
|
||||
end
|
||||
|
||||
it 'replace {domain_name} by its value' do
|
||||
it 'replaces {domain_name} by its value' do
|
||||
expect(finder.potential_urls(opts).keys).to eql %w[
|
||||
http://ex.lo/aa/ex.sql
|
||||
http://ex.lo/aa/wordpress.sql
|
||||
@@ -27,7 +27,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context "when #{sub_domain} sub-domain" do
|
||||
let(:url) { "https://#{sub_domain}.domain.tld" }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/domain.sql"
|
||||
end
|
||||
end
|
||||
@@ -36,7 +36,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context 'when multi-level tlds' do
|
||||
let(:url) { 'https://something.com.tr' }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include 'https://something.com.tr/something.sql'
|
||||
end
|
||||
end
|
||||
@@ -44,7 +44,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context 'when multi-level tlds and sub-domain' do
|
||||
let(:url) { 'https://dev.something.com.tr' }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include 'https://dev.something.com.tr/something.sql'
|
||||
end
|
||||
end
|
||||
@@ -52,10 +52,18 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context 'when some weird stuff' do
|
||||
let(:url) { 'https://098f6bcd4621d373cade4e832627b4f6.aa-bb-ccc-dd.domain-test.com' }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/domain-test.sql"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a non standard URL' do
|
||||
let(:url) { 'http://dc-2' }
|
||||
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/dc-2.sql"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#aggressive' do
|
||||
|
||||
@@ -6,8 +6,55 @@ describe WPScan::Finders::InterestingFindings::MuPlugins do
|
||||
let(:url) { 'http://ex.lo/' }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'mu_plugins') }
|
||||
|
||||
before do
|
||||
expect(target).to receive(:content_dir).at_least(1).and_return('wp-content')
|
||||
end
|
||||
|
||||
describe '#passive' do
|
||||
xit
|
||||
before { stub_request(:get, url).to_return(body: body) }
|
||||
|
||||
context 'when no uris' do
|
||||
let(:body) { '' }
|
||||
|
||||
its(:passive) { should be nil }
|
||||
end
|
||||
|
||||
context 'when a large amount of unrelated uris' do
|
||||
let(:body) do
|
||||
Array.new(250) { |i| "<a href='#{url}#{i}.html'>Some Link</a><img src='#{url}img-#{i}.png'/>" }.join("\n")
|
||||
end
|
||||
|
||||
it 'should not take a while to process the page' do
|
||||
time_start = Time.now
|
||||
result = finder.passive
|
||||
time_end = Time.now
|
||||
|
||||
expect(result).to be nil
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when uris' do
|
||||
let(:body) { File.read(fixtures.join(fixture)) }
|
||||
|
||||
context 'when none matching' do
|
||||
let(:fixture) { 'no_match.html' }
|
||||
|
||||
its(:passive) { should be nil }
|
||||
end
|
||||
|
||||
context 'when matching via href' do
|
||||
let(:fixture) { 'match_href.html' }
|
||||
|
||||
its(:passive) { should be_a WPScan::Model::MuPlugins }
|
||||
end
|
||||
|
||||
context 'when matching from src' do
|
||||
let(:fixture) { 'match_src.html' }
|
||||
|
||||
its(:passive) { should be_a WPScan::Model::MuPlugins }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#aggressive' do
|
||||
|
||||
51
spec/app/finders/passwords/xml_rpc_spec.rb
Normal file
51
spec/app/finders/passwords/xml_rpc_spec.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe WPScan::Finders::Passwords::XMLRPC do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Model::XMLRPC.new(url) }
|
||||
let(:url) { 'http://ex.lo/xmlrpc.php' }
|
||||
|
||||
RESPONSE_403_BODY = '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodResponse>
|
||||
<fault>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>faultCode</name>
|
||||
<value><int>403</int></value>
|
||||
</member>
|
||||
<member>
|
||||
<name>faultString</name>
|
||||
<value><string>Incorrect username or password.</string></value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</fault>
|
||||
</methodResponse>'
|
||||
|
||||
describe '#attack' do
|
||||
let(:wordlist_path) { FINDERS_FIXTURES.join('passwords.txt').to_s }
|
||||
|
||||
context 'when no valid credentials' do
|
||||
before do
|
||||
stub_request(:post, url).to_return(status: status, body: RESPONSE_403_BODY)
|
||||
|
||||
finder.attack(users, wordlist_path)
|
||||
end
|
||||
|
||||
let(:users) { %w[admin].map { |username| WPScan::Model::User.new(username) } }
|
||||
|
||||
context 'when status = 200' do
|
||||
let(:status) { 200 }
|
||||
|
||||
its('progress_bar.log') { should be_empty }
|
||||
end
|
||||
|
||||
context 'when status = 403' do
|
||||
let(:status) { 403 }
|
||||
|
||||
its('progress_bar.log') { should be_empty }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -19,7 +19,7 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#potential_username' do
|
||||
describe '#username_from_response' do
|
||||
[
|
||||
'4.1.1', '4.1.1-permalink',
|
||||
'3.0', '3.0-permalink',
|
||||
@@ -32,6 +32,19 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
|
||||
expect(finder.username_from_response(res)).to eql 'admin'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a lot of unrelated links' do
|
||||
it 'should not take a while to process the page' do
|
||||
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
|
||||
body << '<a href="https://wp.lab/author/test/">Link</a>'
|
||||
|
||||
time_start = Time.now
|
||||
expect(finder.username_from_response(Typhoeus::Response.new(body: body))).to eql 'test'
|
||||
time_end = Time.now
|
||||
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#display_name_from_body' do
|
||||
|
||||
@@ -16,12 +16,31 @@ describe WPScan::Finders::Users::AuthorPosts do
|
||||
|
||||
results = finder.potential_usernames(res)
|
||||
|
||||
expect(results).to eql([
|
||||
expect(results).to eql [
|
||||
['admin', 'Author Pattern', 100],
|
||||
['admin display_name', 'Display Name', 30],
|
||||
['editor', 'Author Pattern', 100],
|
||||
['editor', 'Display Name', 30]
|
||||
])
|
||||
]
|
||||
end
|
||||
|
||||
context 'when a lot of unrelated uris' do
|
||||
it 'should not take a while to process the page' do
|
||||
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
|
||||
body << "<a href='#{url}author/admin/'>Other Link</a>"
|
||||
body << "<a href='#{url}?author=2'>user display name</a>"
|
||||
|
||||
time_start = Time.now
|
||||
results = finder.potential_usernames(Typhoeus::Response.new(body: body))
|
||||
time_end = Time.now
|
||||
|
||||
expect(results).to eql [
|
||||
['admin', 'Author Pattern', 100],
|
||||
['user display name', 'Display Name', 30]
|
||||
]
|
||||
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -202,11 +202,11 @@ describe WPScan::Model::Plugin do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln <= 6.3.10 - LFI',
|
||||
{ wpvulndb: '1' },
|
||||
'LFI',
|
||||
'6.3.10'
|
||||
references: { wpvulndb: '1' },
|
||||
type: 'LFI',
|
||||
fixed_in: '6.3.10'
|
||||
),
|
||||
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
|
||||
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ describe WPScan::Model::Theme do
|
||||
its(:style_uri) { should eql 'http://www.elegantthemes.com/gallery/divi/' }
|
||||
its(:license_uri) { should eql 'http://www.gnu.org/licenses/gpl-2.0.html' }
|
||||
end
|
||||
|
||||
context 'when no tags' do
|
||||
let(:fixture) { fixtures.join('no_tags.css') }
|
||||
|
||||
its(:author) { should eql nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#version' do
|
||||
@@ -224,11 +230,11 @@ describe WPScan::Model::Theme do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln',
|
||||
{ wpvulndb: '1' },
|
||||
'LFI',
|
||||
'6.3.10'
|
||||
references: { wpvulndb: '1' },
|
||||
type: 'LFI',
|
||||
fixed_in: '6.3.10'
|
||||
),
|
||||
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
|
||||
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ describe WPScan::Model::WpItem do
|
||||
end
|
||||
|
||||
it 'encodes the path' do
|
||||
expect(wp_item.url('#t#')).to eql "#{item_url}%23t%23"
|
||||
expect(wp_item.url('#t#')).to eql "#{item_url}#t%23"
|
||||
expect(wp_item.url('t .txt')).to eql "#{item_url}t%20.txt"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,31 +55,15 @@ describe WPScan::Model::WpVersion do
|
||||
expect(version).to be_vulnerable
|
||||
end
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 1',
|
||||
{ wpvulndb: '1' },
|
||||
'SQLI'
|
||||
),
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 2',
|
||||
{ url: %w[url-2 url-3], osvdb: %w[10], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
nil,
|
||||
'3.8.2'
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
context 'when a signle vuln' do
|
||||
let(:number) { '3.8.1' }
|
||||
let(:number) { '3.8' }
|
||||
let(:db_data) { vuln_api_data_for('wordpresses/38') }
|
||||
|
||||
it 'returns the expected result' do
|
||||
@expected = [WPScan::Vulnerability.new(
|
||||
'WP 3.8 - Vuln 1',
|
||||
{ url: %w[url-4], wpvulndb: '3' },
|
||||
'AUTHBYPASS'
|
||||
references: { url: %w[url-4], wpvulndb: '3' },
|
||||
type: 'AUTHBYPASS'
|
||||
)]
|
||||
end
|
||||
end
|
||||
@@ -92,14 +76,14 @@ describe WPScan::Model::WpVersion do
|
||||
@expected = [
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 1',
|
||||
{ wpvulndb: '1' },
|
||||
'SQLI'
|
||||
references: { wpvulndb: '1' },
|
||||
type: 'SQLI',
|
||||
cvss: { score: '5.4', vector: 'VECTOR' }
|
||||
),
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 2',
|
||||
{ url: %w[url-2 url-3], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
nil,
|
||||
'3.8.2'
|
||||
references: { url: %w[url-2 url-3], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
fixed_in: '3.8.2'
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
38759
spec/fixtures/db/dynamic_finders.yml
vendored
38759
spec/fixtures/db/dynamic_finders.yml
vendored
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,11 @@
|
||||
"id" : 1,
|
||||
"vuln_type" : "SQLI",
|
||||
"published_date" : null,
|
||||
"fixed_in" : null
|
||||
"fixed_in" : null,
|
||||
"cvss": {
|
||||
"score": "5.4",
|
||||
"vector": "VECTOR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"references" : {
|
||||
|
||||
6681
spec/fixtures/dynamic_finders/expected.yml
vendored
6681
spec/fixtures/dynamic_finders/expected.yml
vendored
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Woo Pelecard v1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-07-07 07:09+0200\n"
|
||||
"PO-Revision-Date: 2014-07-07 07:09+0200\n"
|
||||
"Last-Translator: Tzvi Rabinovitch <tzvi.ra@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: he_IL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Poedit 1.5.4\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;"
|
||||
"_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"X-Textdomain-Support: yes\n"
|
||||
"X-Poedit-SearchPath-0: ..\n"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:18
|
||||
msgid "Settings saved."
|
||||
msgstr "שמרתי את ההגדרות."
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:23
|
||||
msgid "10Bit EasyCard PayButtons Settings"
|
||||
msgstr "כפתורי תשלום איזיקארד"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:37
|
||||
msgid "Client ID"
|
||||
msgstr "מזהה לקוח"
|
||||
|
||||
# @ TenBit_woo_pelecard
|
||||
#: ../10bit-easycard-paybuttons-options.php:45
|
||||
msgid "Password"
|
||||
msgstr "סיסמה"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:53
|
||||
msgid "Maximum Payments"
|
||||
msgstr "מספר תשלמים מקסימלי"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:64
|
||||
msgid "Save Changes"
|
||||
msgstr "שמור שינויים"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:70
|
||||
msgid "Usage :"
|
||||
msgstr "שימוש"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:72
|
||||
msgid "Add the folloing short code inside a post or a page :"
|
||||
msgstr "הוסיפו את השורטקוד הבא לדף או לעמוד באתר"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:76
|
||||
msgid "value : The amount to pay"
|
||||
msgstr "value : הסכום לתשלום"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:78
|
||||
msgid "item_name : the name of the sold item"
|
||||
msgstr "item_name : שם המוצר למכירה"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:80
|
||||
msgid "button_class : CSS class for styling"
|
||||
msgstr "button_class : קלאס CSS לעיצוב"
|
||||
|
||||
#: ../10bit-easycard-paybuttons-options.php:82
|
||||
msgid "button_text : Text to show on the button"
|
||||
msgstr "button_text : הטקסט שיופיע על הכפתור"
|
||||
|
||||
#: ../10bit-paybuttons-easycard.php:29
|
||||
msgid "EasyCard Pay Buttons"
|
||||
msgstr "כפתורי תשלום איזיקארד"
|
||||
|
||||
# @ TenBit_woo_pelecard
|
||||
#~ msgid "License Key"
|
||||
#~ msgstr "מפתח"
|
||||
|
||||
#~ msgid "Valid"
|
||||
#~ msgstr "תקין"
|
||||
|
||||
#~ msgid "Invalid"
|
||||
#~ msgstr "לא תקין"
|
||||
|
||||
#, fuzzy
|
||||
#~ msgid "Leave blank for not sending a copy"
|
||||
#~ msgstr "השאר ריק במידה ואינך מעוניים בהעתק"
|
||||
|
||||
#, fuzzy
|
||||
#~ msgid "10Bit iCount Settings"
|
||||
#~ msgstr "הגדרות icount"
|
||||
|
||||
#~ msgid "Easycard"
|
||||
#~ msgstr "איזיקארד"
|
||||
@@ -0,0 +1,132 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Woo Pelecard v1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2014-07-09 01:31+0200\n"
|
||||
"PO-Revision-Date: 2014-07-09 01:32+0200\n"
|
||||
"Last-Translator: Tzvi Rabinovitch <tzvi.ra@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: he_IL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Poedit 1.5.4\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;"
|
||||
"_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"X-Textdomain-Support: yes\n"
|
||||
"X-Poedit-SearchPath-0: ..\n"
|
||||
|
||||
# @ TenBit_woo_pelecard
|
||||
#: ../10bit-paybuttons-pelecard.php:28
|
||||
msgid "pelecard Pay Buttons"
|
||||
msgstr "כפתורי תשלום פלאכארד"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:68
|
||||
msgid "Settings saved."
|
||||
msgstr "שמרתי את ההגדרות."
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:73
|
||||
msgid "10Bit pelecard PayButtons Settings"
|
||||
msgstr "כפתורי תשלום פלאכארד"
|
||||
|
||||
# @ TenBit_woo_pelecard
|
||||
#: ../10bit-pelecard-paybuttons-options.php:87
|
||||
msgid "Terminal Number"
|
||||
msgstr "מספר מסוף סליקה"
|
||||
|
||||
# @ TenBit_woo_pelecard
|
||||
#: ../10bit-pelecard-paybuttons-options.php:95
|
||||
msgid "Username"
|
||||
msgstr "שם משתמש"
|
||||
|
||||
# @ TenBit_woo_pelecard
|
||||
#: ../10bit-pelecard-paybuttons-options.php:103
|
||||
msgid "Password"
|
||||
msgstr "סיסמה"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:111
|
||||
msgid "Maximum Payments"
|
||||
msgstr "מספר תשלמים מקסימלי"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:119
|
||||
msgid "Currency"
|
||||
msgstr "מטבע"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:123
|
||||
msgid "ILS"
|
||||
msgstr "ש\"ח"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:124
|
||||
msgid "USD"
|
||||
msgstr "דולר"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:130
|
||||
msgid "Success ( thank you ) page URL"
|
||||
msgstr "כתובת הצלחה ( דף תודה )"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:138
|
||||
msgid "Error page URL"
|
||||
msgstr "כתובת כשלון"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:146
|
||||
msgid "Path to custom logo"
|
||||
msgstr "קישור ללוגו "
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:154
|
||||
msgid "Path to small custom logo"
|
||||
msgstr "קישור ללוגו קטן "
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:162
|
||||
msgid "Hide PCI DSS logo"
|
||||
msgstr "הסתר את הלוגו של PCI DSS"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:170
|
||||
msgid "Hide Pelecard logo"
|
||||
msgstr "הסתר את לוגו פלאכארד"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:178
|
||||
msgid "Path To custom Style sheet(CSS)"
|
||||
msgstr "כתובת כשלון"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:183
|
||||
msgid "must be a secured URL ( HTTPS )"
|
||||
msgstr "חייב להיות מכתובת מאובטחת (HTTPS )"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:189
|
||||
msgid "Background color"
|
||||
msgstr "צבע רקע"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:195
|
||||
msgid "Use hexadecimal color without the #"
|
||||
msgstr "ערך הקסדצימלי ללא #"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:217
|
||||
msgid "Save Changes"
|
||||
msgstr "שמור שינויים"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:223
|
||||
msgid "Usage :"
|
||||
msgstr "שימוש"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:225
|
||||
msgid "Add the following short code inside a post or a page :"
|
||||
msgstr "הוסיפו את השורטקוד הבא לדף או לעמוד באתר"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:229
|
||||
msgid "value : The amount to pay"
|
||||
msgstr "value : הסכום לתשלום"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:231
|
||||
msgid "item_name : the name of the sold item"
|
||||
msgstr "item_name : שם המוצר למכירה"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:233
|
||||
msgid "button_class : CSS class for styling"
|
||||
msgstr "button_class : קלאס CSS לעיצוב"
|
||||
|
||||
#: ../10bit-pelecard-paybuttons-options.php:235
|
||||
msgid "button_text : Text to show on the button"
|
||||
msgstr "button_text : הטקסט שיופיע על הכפתור"
|
||||
13
spec/fixtures/dynamic_finders/plugin_version/24liveblog/composer_file/package.json
vendored
Normal file
13
spec/fixtures/dynamic_finders/plugin_version/24liveblog/composer_file/package.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "liveblog24-live-blogging-tool-cgb-guten-block",
|
||||
"version": "2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "cgb-scripts start",
|
||||
"build": "cgb-scripts build",
|
||||
"eject": "cgb-scripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"cgb-scripts": "1.23.0"
|
||||
}
|
||||
}
|
||||
1418
spec/fixtures/dynamic_finders/plugin_version/2fas/translation_file/languages/2fas-pt_BR.po
vendored
Normal file
1418
spec/fixtures/dynamic_finders/plugin_version/2fas/translation_file/languages/2fas-pt_BR.po
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
# Italian translation for ab-human-time
|
||||
# Copyright (C) 2014 AB Human Time
|
||||
# This file is distributed under the same license as the AB Human Time package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: AB Human Time 0.1\n"
|
||||
"POT-Creation-Date: 2014-02-26 20:13+0100\n"
|
||||
"PO-Revision-Date: 2014-02-26 21:25+0100\n"
|
||||
"Last-Translator: endrix.develop <endrix.develop@gmail.com>\n"
|
||||
"Language-Team: <endrix.develop@gmail.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 1.6.4\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: it_IT\n"
|
||||
|
||||
#: ../ab-human-time.php:21
|
||||
msgid "Published "
|
||||
msgstr "Pubblicato "
|
||||
|
||||
#: ../ab-human-time.php:23
|
||||
msgid " ago"
|
||||
msgstr " fa"
|
||||
@@ -0,0 +1,49 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: xq-xe-xt-xy 1.0\n"
|
||||
"POT-Creation-Date: 2018-07-11 09:44+0300\n"
|
||||
"PO-Revision-Date: 2018-07-11 09:44+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Marko Maksym\n"
|
||||
"Language: uk_UA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"X-Poedit-Basepath: ../includes\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Poedit-KeywordsList: __;_e\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
#: admin/class-admin-main.php:66
|
||||
msgid "Title of the page"
|
||||
msgstr ""
|
||||
|
||||
#: admin/class-admin-main.php:66
|
||||
msgid "Link Name"
|
||||
msgstr ""
|
||||
|
||||
#: admin/class-admin-main.php:69
|
||||
msgid "Submenu title"
|
||||
msgstr ""
|
||||
|
||||
#: admin/class-admin-main.php:69
|
||||
msgid "Submenu item"
|
||||
msgstr ""
|
||||
|
||||
#: admin/templates/index.php:8
|
||||
msgid "Settings Page"
|
||||
msgstr ""
|
||||
|
||||
#: admin/templates/main_module_menu.php:10
|
||||
msgid "Main page"
|
||||
msgstr ""
|
||||
|
||||
#: admin/templates/main_module_menu.php:13 admin/templates/page1.php:8
|
||||
msgid "Page 1"
|
||||
msgstr ""
|
||||
|
||||
#: admin/templates/main_module_menu.php:16 admin/templates/page2.php:8
|
||||
msgid "Page 2"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,254 @@
|
||||
# Copyright 2019
|
||||
# This file is distributed under the GNU General Public License v3 or later.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ACh Updates Manager v1.0.0\n"
|
||||
"POT-Creation-Date: 2019-05-29 10:50+0100\n"
|
||||
"PO-Revision-Date: 2019-05-29 10:50+0100\n"
|
||||
"Last-Translator: Ali Chopani <Chopaniali@gmail.com>\n"
|
||||
"Language-Team: A. Ch <Chopaniali@gmail.com>\n"
|
||||
"Report-Msgid-Bugs-To: Ali Chopani <Chopaniali@gmail.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Textdomain-Support: yes"
|
||||
"X-Generator: Poedit 2.2\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: __;_e;esc_html_e;esc_html__;_x;\n"
|
||||
"X-Poedit-Basepath: ../\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
"X-Poedit-Language: English\n"
|
||||
"X-Poedit-Country: UNITED STATES\n"
|
||||
"X-Poedit-Bookmarks: \n"
|
||||
|
||||
#: ACh-Updates-Manager.php:5
|
||||
msgid "The ACh Updates and Notices Manager is an easy way to manage all your WordPress updates and notifications with one click! e.g. Disable all updates or notifications, Disable automatic updates, Hide errors and warnings messages, Update themes and plugins from zip file and etc."
|
||||
msgstr ""
|
||||
|
||||
#: ACh-Updates-Manager.php:65
|
||||
msgid "Thanks for installing %1$s v%2$s plugin. Click <a href="%3$s">here</a> to configure plugin settings."
|
||||
msgstr ""
|
||||
|
||||
#: ACh-Updates-Manager.php:75
|
||||
msgid "Manage"
|
||||
msgstr ""
|
||||
|
||||
#: ACh-Updates-Manager.php:86
|
||||
#: includes/acupnm-settings.php:34
|
||||
#: includes/acupnm-settings.php:144
|
||||
msgid "Support"
|
||||
msgstr ""
|
||||
|
||||
#: ACh-Updates-Manager.php:87
|
||||
msgid "Donate"
|
||||
msgstr ""
|
||||
|
||||
#: ACh-Updates-Manager.php:103
|
||||
#: includes/acupnm-settings.php:27
|
||||
msgid "ACh Updates and Notices Manager"
|
||||
msgstr ""
|
||||
|
||||
#: ACh-Updates-Manager.php:103
|
||||
msgid "Updates Manager"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:28
|
||||
msgid "The ACh Update Manager plugin for WordPress. Manage all your WordPress updates and notifications with one click!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:33
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:44
|
||||
msgid "✔ Disable all WP updates and notifs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:53
|
||||
msgid "Disable all WordPress core, themes, plugins and translations updates and notifications."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:57
|
||||
msgid "✔ Hide all notices from WP dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:66
|
||||
msgid "Disable all notices from the WordPress dashboard. e.g. errors, updates, warning, rate us, license, dismissible and etc."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:70
|
||||
msgid "✔ Disable plugins updates and notifs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:79
|
||||
msgid "Disable the WordPress plugins updates and notifications."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:83
|
||||
msgid "✔ Disable themes updates and notifs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:92
|
||||
msgid "Disable the WordPress themes updates and notifications."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:96
|
||||
msgid "✔ Disable WP core update and notifs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:105
|
||||
msgid "Disable the WordPress core update and notifications."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:109
|
||||
msgid "✔ Hide WordPress core update notice"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:118
|
||||
msgid "Hide WordPress core update notice from WP dashboard."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:122
|
||||
msgid "✔ Update theme and plugin from zip"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:131
|
||||
msgid "This feature allows you to update plugins and themes using a zip file. While upgrading, a backup copy of the old theme or plugin is first created. This allows you to install the old version in case of problems with the new version."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:147
|
||||
msgid "If you need assistance, see our help resources."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:148
|
||||
msgid "Please make a search to find help with your problem, or head over to our support forum to ask a question."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:152
|
||||
msgid "Visit my site"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:155
|
||||
msgid "Send email"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:158
|
||||
msgid "Support forum"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:167
|
||||
msgid "About us"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:170
|
||||
msgid "The ACh Updates and Notices Manager is an easy way to manage all your WordPress updates and notifications with one click!"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:171
|
||||
msgid "ACh Updates and Notices Manager was developed by <a class="achupnm-link-text" href="https://ach.li" target="_blank">A. Ch</a> and is <a class="achupnm-link-text" href="https://wordpress.org" target="_blank">available for free</a> on WordPress."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:172
|
||||
msgid "We work hard to give you an exceptional premium products and 5 star support. To show your appreciation you can buy us a coffee or simply by sharing or follow us on social media."
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:176
|
||||
msgid "Buy us a coffee"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:177
|
||||
msgid "Like us"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:178
|
||||
msgid "Tweet us"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:179
|
||||
msgid "Rate us"
|
||||
msgstr ""
|
||||
|
||||
#: includes/acupnm-settings.php:191
|
||||
msgid "Save Changes"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:41
|
||||
msgid "Upgrading the plugin…"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:42
|
||||
msgid "Backing up the old version of the plugin…"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:88
|
||||
msgid "A backup zip file of the old plugin version can be downloaded <a href="%1$s">here</a>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:98
|
||||
msgid "Moving the old version of the plugin to a new directory…"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:114
|
||||
msgid "Unable to find a new directory name to move the old version of the plugin to. No backup will be created."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:120
|
||||
msgid "Moved the old version of the plugin to a new plugin directory named %1$s. This directory should be backed up and removed from the site."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:122
|
||||
msgid "Unable to move the old version of the plugin to a new directory. No backup will be created."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:142
|
||||
msgid "A plugin backup can not be created since a destination path for the backup file could not be found."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:167
|
||||
msgid "A plugin backup can not be created as creation of the zip file failed with the following error: %1$s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-plugin-updater.php:174
|
||||
msgid "Plugin Backup - %1$s - %2$s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:41
|
||||
msgid "Upgrading the theme…"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:42
|
||||
msgid "Backing up the old version of the theme…"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:60
|
||||
msgid "A backup zip file of the old theme version can be downloaded <a href="%1$s">here</a>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:70
|
||||
msgid "Moving the old version of the theme to a new directory…"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:91
|
||||
msgid "Unable to find a new directory name to move the old version of the theme to. No backup will be created."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:97
|
||||
msgid "Moved the old version of the theme to a new theme directory named %1$s. This directory should be backed up and removed from the site."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:99
|
||||
msgid "Unable to move the old version of the theme to a new directory. No backup will be created."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:119
|
||||
msgid "A theme backup can not be created since a destination path for the backup file could not be found."
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:148
|
||||
msgid "A theme backup can not be created as creation of the zip file failed with the following error: %1$s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/easy-update/ach-theme-updater.php:155
|
||||
msgid "Theme Backup - %1$s - %2$s"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2020 Acowebs
|
||||
# This file is distributed under the same license as the Product Labels For Woocommerce plugin.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Product Labels For Woocommerce 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/woocomerce-sales-badge\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2020-07-09T05:22:44+00:00\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"X-Generator: WP-CLI 2.3.0\n"
|
||||
"X-Domain: aco-product-labels-for-woocommerce\n"
|
||||
|
||||
#. Plugin Name of the plugin
|
||||
#. Description of the plugin
|
||||
msgid "Product Labels For Woocommerce"
|
||||
msgstr ""
|
||||
|
||||
#. Author of the plugin
|
||||
msgid "Acowebs"
|
||||
msgstr ""
|
||||
|
||||
#. Author URI of the plugin
|
||||
msgid "http://acowebs.com"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-acoplw-backend.php:130
|
||||
#: includes/class-acoplw-backend.php:144
|
||||
msgid "Badges"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-acoplw-backend.php:132
|
||||
#: includes/class-acoplw-backend.php:145
|
||||
msgid "Product Lists"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-acoplw-backend.php:134
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,112 @@
|
||||
# Copyright (C) 2016 Kailey Lampert
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Kailey Lampert <trepmal@gmail.com>, 2016.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.7\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-09-08 17:48-0700\n"
|
||||
"PO-Revision-Date: 2016-09-08 17:48-0700\n"
|
||||
"Last-Translator: Kailey Lampert <trepmal@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: active-plugins.php:62
|
||||
msgid "Acitve Plugins is for multisite use only."
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:72 active-plugins.php:83
|
||||
msgid "Active Plugins Across Network"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:72
|
||||
msgid "Active Plugins"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:86
|
||||
#, php-format
|
||||
msgid "<a href=\"%s\">Network-Activated Plugins</a>"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:87
|
||||
#, php-format
|
||||
msgid "<a href=\"%s\">MU Plugins</a>"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:90
|
||||
#, php-format
|
||||
msgid "List does not include: %s"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:146
|
||||
msgid ""
|
||||
"Totals <span class=\"description\">each active plugin and how many users</"
|
||||
"span>"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:176 active-plugins.php:213
|
||||
#, php-format
|
||||
msgid "v%s"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:180 active-plugins.php:214
|
||||
#, php-format
|
||||
msgid "%1$s %2$s"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:182
|
||||
#, php-format
|
||||
msgid "%s (Uninstalled)"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:187
|
||||
#, php-format
|
||||
msgid " (tagged: %s)"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:197
|
||||
msgid "Select all"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:198
|
||||
msgid "Deselect all"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:199
|
||||
msgid "Toggle"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:209
|
||||
msgid "Plugins with zero (0) users:"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:217
|
||||
msgid "none"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:224
|
||||
msgid "Show sites with no active plugins"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:235
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:236
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:237
|
||||
msgid "Dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:238
|
||||
msgid "Plugins"
|
||||
msgstr ""
|
||||
|
||||
#: active-plugins.php:249
|
||||
msgid "(network-activated)"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,111 @@
|
||||
# Copyright (C) 2020 PRESSMAN
|
||||
# This file is distributed under the same license as the Admin User Control plugin.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Admin User Control 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/admin-user-"
|
||||
"control\n"
|
||||
"POT-Creation-Date: 2020-05-18T02:23:41+00:00\n"
|
||||
"PO-Revision-Date: 2020-05-18 11:26+0900\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: ja\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.3\n"
|
||||
"X-Domain: admin-user-control\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. Plugin Name of the plugin
|
||||
msgid "Admin User Control"
|
||||
msgstr "Admin User Control"
|
||||
|
||||
#. Description of the plugin
|
||||
msgid "Control admin user in administration screens in real time."
|
||||
msgstr "管理画面にログインしているユーザーをリアルタイムで制御します。"
|
||||
|
||||
#. Author of the plugin
|
||||
msgid "PRESSMAN"
|
||||
msgstr "PRESSMAN"
|
||||
|
||||
#. Author URI of the plugin
|
||||
msgid "https://www.pressman.ne.jp"
|
||||
msgstr "https://www.pressman.ne.jp"
|
||||
|
||||
#: auc-login-monitor.php:22
|
||||
msgid "Logged in"
|
||||
msgstr "ログイン"
|
||||
|
||||
#: auc-maintenance.php:38 auc-maintenance.php:174
|
||||
msgid "Maintenance"
|
||||
msgstr "メンテナンス"
|
||||
|
||||
#: auc-maintenance.php:42 auc-maintenance.php:220
|
||||
msgid "maintenance_start_date"
|
||||
msgstr "メンテナンス開始日時"
|
||||
|
||||
#: auc-maintenance.php:59 auc-maintenance.php:221
|
||||
msgid "maintenance_end_date"
|
||||
msgstr "メンテナンス終了日時"
|
||||
|
||||
#: auc-maintenance.php:114
|
||||
msgid "Forced logout after 10 seconds because the maintenance start time"
|
||||
msgstr "メンテナンスの開始時刻になったため10秒後に強制ログアウトします"
|
||||
|
||||
#: auc-maintenance.php:115
|
||||
msgid "Scheduled maintenance end time"
|
||||
msgstr "メンテナンス終了予定時間"
|
||||
|
||||
#: auc-maintenance.php:175
|
||||
msgid "Add New Maintenance"
|
||||
msgstr "メンテナンスを追加"
|
||||
|
||||
#: auc-maintenance.php:176
|
||||
msgid "Edit Maintenance"
|
||||
msgstr "メンテナンスの編集"
|
||||
|
||||
#: auc-maintenance.php:246
|
||||
msgid "Latest maintenance information"
|
||||
msgstr "直近のメンテナンス情報"
|
||||
|
||||
#: auc-maintenance.php:269
|
||||
msgid "Click a title to display a detail."
|
||||
msgstr "タイトルをクリックすると詳細が表示されます。"
|
||||
|
||||
#: auc-maintenance.php:287
|
||||
msgid "No maintenance information"
|
||||
msgstr "メンテナンス情報がありません"
|
||||
|
||||
#: auc-notification.php:26
|
||||
msgid "New notification"
|
||||
msgstr "新しいお知らせがあります"
|
||||
|
||||
#: auc-notification.php:50
|
||||
msgid "Notification"
|
||||
msgstr "お知らせ"
|
||||
|
||||
#: auc-notification.php:51
|
||||
msgid "Add New Notification"
|
||||
msgstr "お知らせを追加"
|
||||
|
||||
#: auc-notification.php:52
|
||||
msgid "Edit Notification"
|
||||
msgstr "お知らせの編集"
|
||||
|
||||
#: auc-notification.php:70
|
||||
msgid "Latest notifications"
|
||||
msgstr "直近のお知らせ"
|
||||
|
||||
#: auc-notification.php:86
|
||||
msgid "Click a title to display a detail. If you read it, mark it as read."
|
||||
msgstr ""
|
||||
"タイトルをクリックすると詳細が表示されます。読んだら既読にしてください。"
|
||||
|
||||
#: auc-notification.php:91
|
||||
msgid "Mark as read"
|
||||
msgstr "既読にする"
|
||||
|
||||
#: auc-notification.php:113
|
||||
msgid "There is no notifications"
|
||||
msgstr "お知らせがありません"
|
||||
@@ -0,0 +1,428 @@
|
||||
# Copyright (C) 2012
|
||||
# This file is distributed under the same license as the package.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Advanced Category Column\n"
|
||||
"Report-Msgid-Bugs-To: http://wordpress.org/tag/advanced-category-column\n"
|
||||
"POT-Creation-Date: 2016-02-17 11:35+0200\n"
|
||||
"PO-Revision-Date: 2016-02-17 11:36+0200\n"
|
||||
"Last-Translator: Stefan Crämer <translate@atelier-fuenf.de>\n"
|
||||
"Language-Team: Waldemar Stoffel <stoffel@atelier-fuenf.de>\n"
|
||||
"Language: es_ES\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Generator: Poedit 1.8.1\n"
|
||||
"X-Poedit-Basepath: ..\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
|
||||
"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
|
||||
"_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
#: advanced-cc.php:112
|
||||
msgid "FAQ"
|
||||
msgstr "FAQ"
|
||||
|
||||
#: advanced-cc.php:113
|
||||
msgid "Donate"
|
||||
msgstr "Donar"
|
||||
|
||||
#: advanced-cc.php:123 class-lib/A5_OptionPageClass.php:31
|
||||
#: class-lib/ACC_AdminClass.php:35
|
||||
msgid "Settings"
|
||||
msgstr "Ajustes"
|
||||
|
||||
#: class-lib/A5_ImageClass.php:48
|
||||
msgid "Permalink to"
|
||||
msgstr "enlace permanente a"
|
||||
|
||||
#: class-lib/A5_OptionPageClass.php:145
|
||||
msgid "Click to toggle"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_OptionPageClass.php:382
|
||||
msgid "Not set"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:37
|
||||
msgid "Homepage"
|
||||
msgstr "Página de Inicio"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:38
|
||||
msgid "Frontpage (e.g. a static page as homepage)"
|
||||
msgstr ""
|
||||
"Página Inicial (por ejemplo, una página estática como página de inicio)"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:39
|
||||
msgid ""Page" pages"
|
||||
msgstr ""Pagína" páginas"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:40
|
||||
msgid "Category pages"
|
||||
msgstr "Categoría de páginas"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:41
|
||||
msgid "Single post pages"
|
||||
msgstr "Páginas de post simples"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:42
|
||||
msgid "Archive pages"
|
||||
msgstr "Páginas de Archivo"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:43
|
||||
#, fuzzy
|
||||
msgid "Post type archives"
|
||||
msgstr "Páginas de post simples"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:44
|
||||
msgid "Tag pages"
|
||||
msgstr "Páginas de Etiquetas"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:45
|
||||
msgid "Attachments"
|
||||
msgstr "Adjuntos"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:46
|
||||
msgid "Custom Taxonomy pages (only available, if having a plugin)"
|
||||
msgstr ""
|
||||
"Páginas de Taxonomía Personalizada (sólo está disponible si tiene un plugin)"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:47
|
||||
msgid "Author pages"
|
||||
msgstr "Páginas del autor"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:48
|
||||
msgid "Search Results"
|
||||
msgstr "Buscar Resultados"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:49
|
||||
msgid ""Not Found""
|
||||
msgstr ""No Encontrado""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:52
|
||||
#, fuzzy
|
||||
msgid "Login Page (only available, if having a plugin)"
|
||||
msgstr ""
|
||||
"Páginas de Taxonomía Personalizada (sólo está disponible si tiene un plugin)"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:54
|
||||
msgid "Check all"
|
||||
msgstr "Seleccionar todo"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:56
|
||||
msgid ""
|
||||
"Check, where you want to show the widget. By default, it is showing on the "
|
||||
"homepage and the category pages:"
|
||||
msgstr ""
|
||||
"Seleccione, dónde desea mostrar el widget. Por defecto, se muestra en la "
|
||||
"página principal y en las páginas de la categoría:"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:92
|
||||
msgid "Under image"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:92
|
||||
msgid "Left of image"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:92
|
||||
msgid "Right of image"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:92
|
||||
msgid "Don't show excerpt"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:94
|
||||
msgid ""
|
||||
"Choose, whether or not to display the excerpt and whether it comes under the "
|
||||
"thumbnail or next to it."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:104
|
||||
msgid "Weight of the Post Title:"
|
||||
msgstr "Peso del título del Post:"
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:108
|
||||
msgid "Left"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:108
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:108
|
||||
msgid "Center"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:108
|
||||
msgid "Justify"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:110
|
||||
msgid "How do you want to align the Post Title?"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:121
|
||||
msgid ""
|
||||
"Check to have an additional 'read more' link at the end of the "
|
||||
"excerpt."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:122
|
||||
#, php-format
|
||||
msgid ""
|
||||
"Write here some text for the 'read more' link. By default, it is %s:"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/A5_WidgetClass.php:123
|
||||
msgid ""
|
||||
"If you want to style the 'read more' link, you can enter a class "
|
||||
"here."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:63
|
||||
msgid ""
|
||||
"http://wasistlos.waldemarstoffel.com/plugins-fur-wordpress/advanced-category-"
|
||||
"column-plugin"
|
||||
msgstr ""
|
||||
"http://wasistlos.waldemarstoffel.com/plugins-fur-wordpress/advanced-category-"
|
||||
"column-plugin"
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:63
|
||||
msgid "Plugin Support"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:65
|
||||
msgid ""
|
||||
"Style the links of the widget. If you leave this empty, your theme will "
|
||||
"style the hyperlinks."
|
||||
msgstr ""
|
||||
"El estilo de los enlaces del widget. Si deja el campo vacío, el tema será el "
|
||||
"mismo estilo de los hipervínculos."
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:67
|
||||
msgid "Just input something like,"
|
||||
msgstr "Sólo algo de entrada como,"
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:71
|
||||
msgid "to get fat, blue, underlined links."
|
||||
msgstr "Los enlaces serán, azul subrayado."
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:73
|
||||
msgid ""
|
||||
"You most probably have to use "!important" at the end of each line, "
|
||||
"to make it work."
|
||||
msgstr ""
|
||||
"Lo más probable es usar "!important" al final de cada línea, para "
|
||||
"hacer que funcione."
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:86
|
||||
msgid "Debug Info"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:105
|
||||
msgid "Styling of the links"
|
||||
msgstr "Estilo de los enlaces"
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:107
|
||||
msgid "Link style:"
|
||||
msgstr "Estílo enlace:"
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:109
|
||||
msgid "Hover style:"
|
||||
msgstr "Estilo hover:"
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:111
|
||||
msgid "Widget container:"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:111
|
||||
msgid ""
|
||||
"You can enter your own style for the widgets here. This will overwrite the "
|
||||
"styles of your theme."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:111
|
||||
msgid ""
|
||||
"If you leave this empty, you can still style every instance of the widget "
|
||||
"individually."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:113
|
||||
#, fuzzy
|
||||
msgid "Compress Style Sheet:"
|
||||
msgstr "Estílo enlace:"
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:113
|
||||
msgid "Click here to compress the style sheet."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:115
|
||||
msgid "Debug:"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:115
|
||||
msgid ""
|
||||
"If you can't reach the dynamical style sheet, you'll have to display "
|
||||
"the styles inline. By clicking here you can do so."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:119
|
||||
msgid "entries"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:119
|
||||
msgid "entry"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:121
|
||||
#, php-format
|
||||
msgid "Empty cache (%d %s):"
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:121
|
||||
msgid "You can empty the plugin's cache here, if necessary."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:129
|
||||
msgid "Just put some css code here."
|
||||
msgstr "Sólo hay que poner algo de código CSS aquí."
|
||||
|
||||
#: class-lib/ACC_AdminClass.php:189
|
||||
msgid "Cache emptied."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:18
|
||||
msgid ""
|
||||
"Configure the output and looks of the widget. Then display thumbnails and "
|
||||
"excerpts of posts in your sidebars and define, on what kind of pages they "
|
||||
"will show."
|
||||
msgstr ""
|
||||
"Configure la salida y la apariencia del widget. Entonces se mostrarán las "
|
||||
"miniaturas y extractos de los mensajes en las barras laterales y se definirá "
|
||||
"en qué tipo de páginas se mostrarán."
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:108
|
||||
msgid "Title:"
|
||||
msgstr "Título:"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:109
|
||||
#, php-format
|
||||
msgid ""
|
||||
"To exclude certain categories or to show just a special category, simply "
|
||||
"write their ID's separated by comma (e.g. %s-5, 2, 4%s will show "
|
||||
"categories 2 and 4 and will exclude category 5):"
|
||||
msgstr ""
|
||||
"Para excluir ciertas categorías o para mostrar sólo una categoría especial, "
|
||||
"basta con escribir su ID separado por coma (por ejemplo,%s-5, 2, 4%s "
|
||||
"mostrará las categorías 2 y 4, y se excluye la categoría 5):"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:110
|
||||
msgid "Check to show the categories in which the post is filed."
|
||||
msgstr "Seleccione para mostrar las categorías en que se presenta el mensaje."
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:111
|
||||
msgid ""
|
||||
"Give some text that you want in front of the post's categtories (i.e "
|
||||
"'filed under':"
|
||||
msgstr ""
|
||||
"De algún texto que usted quiere al frente de la categoría de mensajes (ej. "
|
||||
"'llenado debajo':"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:112
|
||||
msgid "How many posts will be displayed in the sidebar:"
|
||||
msgstr "Cuántos post se mostrarán en la barra lateral:"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:113
|
||||
msgid "Offset (how many posts are spared out in the beginning):"
|
||||
msgstr "Offset (cuántos post están a salvo en el inicio):"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:114
|
||||
msgid "Check to have the offset only on your homepage."
|
||||
msgstr ""
|
||||
"Verificación para que el desplazamiento sólo sea en la página principal."
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:115
|
||||
msgid "Width of the thumbnail (in px):"
|
||||
msgstr "Ancho de las miniaturas (en px)"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:116
|
||||
#, php-format
|
||||
msgid ""
|
||||
"If wanting a border around the image, write the style here. %s would make it "
|
||||
"a black border, 1px wide."
|
||||
msgstr ""
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:118
|
||||
msgid "In case there is no excerpt defined, how many sentences are displayed:"
|
||||
msgstr ""
|
||||
"En caso de que no haya extracto definido, como cuantas frases se muestran:"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:119
|
||||
msgid "Check to display words instead of sentences."
|
||||
msgstr "Seleccione para mostrar palabras en lugar de oraciones."
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:120
|
||||
msgid "Check to have each sentense in a new line."
|
||||
msgstr "Seleccione para tener cada sentencia en una línea nueva."
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:122
|
||||
msgid ""
|
||||
"Check to return the excerpt unfiltered (might avoid interferences with other "
|
||||
"plugins)."
|
||||
msgstr ""
|
||||
"Seleccione para retornar el extracto sin filtrar (puede evitar "
|
||||
"interferencias con otros plugins)."
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:123
|
||||
msgid ""
|
||||
"If you want a line between the posts, this is the height in px (if not "
|
||||
"wanting a line, leave emtpy):"
|
||||
msgstr ""
|
||||
"Si usted quiere una línea entre los posts, esta es la altura en píxeles (si "
|
||||
"no quisiera una línea, deje en blanco)"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:124
|
||||
msgid "The color of the line (e.g. #cccccc):"
|
||||
msgstr "El color de la línea (e.g. #cccccc):"
|
||||
|
||||
#: class-lib/ACC_WidgetClass.php:126
|
||||
#, fuzzy, php-format
|
||||
msgid ""
|
||||
"Here you can finally style the widget. Simply type something like%sto get "
|
||||
"just a gray outline and a padding of 10 px. If you leave that section empty, "
|
||||
"your theme will style the widget."
|
||||
msgstr ""
|
||||
"Aquí usted puede finalmente ver el estilo del widget. Simplemente escriba "
|
||||
"algo como%1$s%2$sborder-left: 1px dashed;%2$sborder-color: #000000;%3$s"
|
||||
"%2$spara obtener una línea de puntos negro de la izquierda. Si deja la "
|
||||
"sección vacía, su tema le dará estilo al widget."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The Advanced Category Column does, what the Category Column Plugin does; "
|
||||
#~ "it creates a widget, which you can drag to your sidebar and it will show "
|
||||
#~ "excerpts of the posts of other categories than showed in the center-"
|
||||
#~ "column. It just has more options than the the Category Column Plugin. It "
|
||||
#~ "is tested with WP up to version 3.5 and it might work with versions down "
|
||||
#~ "to 2.9, but will never be explicitly supported for those. The 'Advanced' "
|
||||
#~ "means, that you have a couple of more options than in the 'Category "
|
||||
#~ "Column Plugin'."
|
||||
#~ msgstr ""
|
||||
#~ "La Columna Categoría Avanzada hace, lo que hace el Plugin de Columna de "
|
||||
#~ "Categoría, se crea un widget, que se puede arrastrar a la barra lateral y "
|
||||
#~ "se mostrarán extractos de los mensajes de otras categorías que se "
|
||||
#~ "mostraron en la columna del centro. Simplemente tiene más opciones que el "
|
||||
#~ "Plugin de Columna de Categoría. Se probó con WP hasta la versión 3.5 y "
|
||||
#~ "que podría funcionar con las versiones hasta la 2.9, pero nunca se probó "
|
||||
#~ "explícitamente en aquellos. Los menús 'Avanzados' significan, que tiene "
|
||||
#~ "un par de opciones más que el 'Plugin de Columna de Categoría'."
|
||||
|
||||
#~ msgid "Waldemar Stoffel"
|
||||
#~ msgstr "Waldemar Stoffel"
|
||||
|
||||
#~ msgid "http://www.waldemarstoffel.com"
|
||||
#~ msgstr "http://www.waldemarstoffel.com"
|
||||
@@ -0,0 +1,73 @@
|
||||
# Copyright (C) 2020 Condless
|
||||
# This file is distributed under the same license as the Advanced Options for WooCommerce plugin.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Advanced Options for WooCommerce 1.0.1\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/advanced-options-"
|
||||
"for-woocommerce\n"
|
||||
"Language-Team: Condless <info@condless.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2020-04-24 08:53+0300\n"
|
||||
"PO-Revision-Date: 2020-04-24 08:55+0300\n"
|
||||
"X-Generator: Poedit 2.3\n"
|
||||
"X-Domain: advanced-options-for-woocommerce\n"
|
||||
"Last-Translator: Condless <info@condless.com>\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : n==2 ? 1 : n>10 && n%10==0 ? "
|
||||
"2 : 3);\n"
|
||||
"Language: he_IL\n"
|
||||
|
||||
#. Plugin Name of the plugin
|
||||
msgid "Advanced Options for WooCommerce"
|
||||
msgstr "אפשרויות מתקדמות לווקומרס"
|
||||
|
||||
#. Plugin URI of the plugin
|
||||
msgid "https://en.condless.com/advanced-options-for-woocommerce/"
|
||||
msgstr "https://www.condless.com/advanced-options-for-woocommerce/"
|
||||
|
||||
#. Description of the plugin
|
||||
msgid ""
|
||||
"WooCommerce plugin for more options and customizations. Simple and Easy to "
|
||||
"use."
|
||||
msgstr "תוסף לווקומרס לאפשרויות נוספות והתאמה אישית. פשוט וקל לשימוש."
|
||||
|
||||
#. Author of the plugin
|
||||
msgid "Condless"
|
||||
msgstr "Condless"
|
||||
|
||||
#. Author URI of the plugin
|
||||
msgid "https://www.condless.com/"
|
||||
msgstr "https://www.condless.com/"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:163
|
||||
msgid "contains letters not from the permitted languages"
|
||||
msgstr "מכיל אותיות שלא שייכות לשפות המותרות"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:200
|
||||
msgid "Advanced Options"
|
||||
msgstr "אפשרויות מתקדמות"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:235
|
||||
msgid ""
|
||||
"Allow only ASCII chars (which prevent non-English letters) in the checkout "
|
||||
"fields"
|
||||
msgstr "אפשר רק תווי ASCII (מה שמונע אותיות שלא באנגלית) בשדות התשלום"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:241
|
||||
msgid ""
|
||||
"Display the VAT amount and the product price include & exlude it in single "
|
||||
"product page"
|
||||
msgstr "הצג את סכום המע\"מ ואת מחיר המוצר איתו ובלעדיו בעמוד מוצר"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:247
|
||||
msgid "Display for variable products the attribute name instead of"
|
||||
msgstr "הצג במוצרים עם וריאיציות את שם התכונה במקום"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:253
|
||||
msgid "Display only products in"
|
||||
msgstr "הצג מוצרים בלבד ב"
|
||||
|
||||
#: advanced-options-for-woocommerce.php:259
|
||||
msgid "Auto triggered by"
|
||||
msgstr "פועל אוטומטית כאשר"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user