我正在尝试使用 python 将文件上传到 WordPress 网站。到目前为止,我似乎无法在没有扩展的情况下将 API 与会话 cookie 一起使用。所以此时我正在尝试遵循以下帖子
这是我到目前为止所拥有的。
#!/usr/bin/python3
import sys, requests
f = 'test.txt'
user='username'
password='password'
url1='https://example.com/wp-login.php'
url2='https://example.com/wp-admin/media-new.php'
headerauth= {'Cookie':'wordpress_test_cookie=WP Cookie check'}
dataauth = {'log':user, 'pwd':password, 'wp-submit':'Log In'}
dataupload = {'post_id': '0', '_wp_http_referer': '/wp-admin/media-new.php', 'action': 'upload_attachement', 'html-upload': 'Upload'}
image = {'async-upload':('test.txt', open(f, "rb"))}
session1=requests.session()
r1 = session1.post(url1, headers=headerauth, data=dataauth)
print(r1)
r2 = session1.get(url2)
print(r2)
r3 = session1.post(url2, data=dataupload, files=image)
print(r3)
运行此程序时,我得到以下响应,显然最后一个响应很有趣。
./upload.py
<Response [200]>
<Response [200]>
<Response [403]>
我还尝试在手动上传文件后从 Chrome 中提取数据字段,直接发布到 async-upload.php 并获得类似的结果。
更新: 我收到的回复页面的标题如下。
<title>Something went wrong.</title>
...
<body id="error-page">
<div class="wp-die-message">The link you followed has expired.</div>
</body>
在挖掘页面的源代码后,我还添加了随机数值。 这是我发现的
<input type="hidden" id="_wpnonce" name="_wpnonce" value="74bdb561c5">
这是我添加的。
r2 = session1.get(url2)
test = re.search('value="[0-9a-z]{10}"', r2.text)
nonce = re.search('[0-9a-z]{10}', test.group(0))
nonce = nonce.group(0)
dataupload = {'post_id':'0', '_wp_http_referer':'/wp-admin/media-new.php', '_wpnonce':nonce, 'action':'upload_attachement', 'html-upload':'Upload'}
仍然没有运气。我还注意到,与基于浏览器的会话相比,缺少 cookie。我假设我实际上并没有进行身份验证。
您可能需要在请求中添加更多标头。
标题可以在
Network > Headers > Request Headers of the Developer Tools.
中找到(按 F12 进行切换。)
使用了错误的随机数。要提取正确的随机数,请使用以下命令并将其发布到 async-upload.php。关键是从 media-new.php 页面中提取 _wpnonce 表单。
如果您不从表单参数中提取,那么您最终可能会得到十几个其他随机数之一。
test = re.search('"multipart_params":.*_wpnonce":"[0-9a-z]+"', r1.text)
nonce = re.search('(?<=_wpnonce":")[0-9a-z]{10}', test.group(0))
nonce = nonce.group(0)
完整代码为
#!/usr/bin/python3
import sys, requests, re
f = 'test.txt'
user='user'
password='password'
url1='https://example.com/wp-login.php'
redirecturl='https://example.com/wp-admin/media-new.php'
url2='https://example.com/wp-admin/async-upload.php'
headerauth= {
'Cookie':'wordpress_test_cookie=WP Cookie check; ROUTEID=.1',
'Host':'example.com',
'Content-Type': 'application/x-www-form-urlencoded'
}
dataauth = {
'log':user,
'pwd':password,
'wp-submit':'Log In',
'redirect_to': redirecturl,
'testcookie': 1
}
image = {'async-upload':('test.txt', open(f, "rb"))}
testimage = open(f, "rb")
session1=requests.session()
session1.get(url1)
r1 = session1.post(url1, headers=headerauth, data=dataauth)
test = re.search('"multipart_params":.*_wpnonce":"[0-9a-z]+"', r1.text)
nonce = re.search('(?<=_wpnonce":")[0-9a-z]{10}', test.group(0))
nonce = nonce.group(0)
uploadheaders = {
'Connection': 'keep-alive',
'Referer': 'https://example.com/wp-admin/upload.php',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin'
}
dataupload = {
'name': 'test.txt',
'action': 'upload-attachement',
'_wpnonce': nonce,
'wpmf_folder': '0',
}
r2 = session1.post(url2, data=dataupload, headers=uploadheaders, files=image)
在 WordPress API v2 上使用此 Python 3 脚本测试多种文件类型后,我们发布了一个要点。它上传到 WordPress 默认上传目录,当然您可以在以下位置修改其设置:
WordPress Dashboard > Settings > Media
此脚本也以安全的方式处理令牌。
希望它也适合你:
### upload.py
import os
import base64
import requests
from credentials import get_wp_access
def upload_file(relative_path, caption, destination):
# Get the absolute path to the directory containing this script
project_dir = os.path.dirname(os.path.abspath(__file__))
# Combine the project directory with the relative path to get the full system path
file_path = os.path.join(project_dir, relative_path)
api_url, username, password = get_wp_access(destination)
credentials = username + ':' + password
token = base64.b64encode(credentials.encode())
json_header = {'Authorization': 'Basic ' + token.decode('utf-8')}
media = {'file': open(file_path, "rb"), 'caption': caption}
response = requests.post(api_url + "media", headers=json_header, files=media)
print(response.text)
# Example usage with a path relevant to the project directory
upload_file('relational/path/to/file.pptx', 'Sample caption', 'site_1')
在另一个文件中:
### credentials.py
destinations = {
"site_1": {
"api_url": "https://example-1.com/wp-json/wp/v2/",
"username": "wp_username",
"password": "XXXX XXXX XXXX XXXX XXXX XXXX"
},
"site_2": {
"api_url": "https://example-2.com/wp-json/wp/v2/posts",
"username": "wp_username",
"password": "XXXX XXXX XXXX XXXX XXXX XXXX"
},
"site_3": {
"api_url": "https://example-3.com/wp-json/wp/v2/posts",
"username": "wp_username",
"password": "XXXX XXXX XXXX XXXX XXXX XXXX"
},
"site_4": {
"api_url": "https://example-4.com/wp-json/wp/v2/posts",
"username": "wp_username",
"password": "XXXX XXXX XXXX XXXX XXXX XXXX"
},
"site_5": {
"api_url": "https://example-5.com/wp-json/wp/v2/posts",
"username": "wp_username",
"password": "XXXX XXXX XXXX XXXX XXXX XXXX"
},
# More websites if needed...
}
def get_wp_access(destination):
access_info = destinations.get(destination, {})
api_url = access_info.get("api_url", "")
username = access_info.get("username", "")
password = access_info.get("password", "")
return api_url, username, password
使用此脚本,您可以拥有多个 WordPress 网站的字典,并使用单个函数更新所有网站。如果您想要一个全合一模块,请随意将脚本组合在一起。