Web


Nmap discovered a Web server on the target port 12340 The running service is Apache httpd 2.4.6

Webroot returns 404

Fuzzing


┌──(kali㉿kali)-[~/archive/thm/zeno]
└─$ ffuf -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -t 200 -u http://$IP:12340/FUZZ -ic
________________________________________________
 :: Method           : GET
 :: URL              : http://10.10.20.195:12340/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 200
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
.htaccess               [Status: 403, Size: 211, Words: 15, Lines: 9, Duration: 225ms]
.htpasswd               [Status: 403, Size: 211, Words: 15, Lines: 9, Duration: 396ms]
rms                     [Status: 301, Size: 238, Words: 14, Lines: 8, Duration: 34ms]
:: Progress: [20476/20476] :: Job [1/1] :: 211 req/sec :: Duration: [0:00:51] :: Errors: 0 ::

ffuf discovered a directory; rms

/rms/


Heading over to the /rms/ directory reveals a web application for a restaurant rms might stand for restaurant management system

Authentication


Testing the authentication returns a popup message via the alert() function without even interacting with the backend. It’s entirely possible that some functions are handled on the client-side

Indeed, there is the user.js file

user.js


┌──(kali㉿kali)-[~/archive/thm/zeno]
└─$ curl -s http://10.10.20.195:12340/rms/validation/user.js 
//function to handle login-form validation
function loginValidate(loginForm){
 
var validationVerified=true;
var errorMessage="";
 
if (loginForm.login.value=="")
{
errorMessage+="Email not filled!\n";
validationVerified=false;
}
if(loginForm.password.value=="")
{
errorMessage+="Password not filled!\n";
validationVerified=false;
}
if (!isValidEmail(loginForm.login.value)) {
errorMessage+="Invalid email address provided!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//function to handle register-form validation
function registerValidate(registerForm){
 
var validationVerified=true;
var errorMessage="";
 
if (registerForm.fname.value=="")
{
errorMessage+="Firstname not filled!\n";
validationVerified=false;
}
if(registerForm.lname.value=="")
{
errorMessage+="Lastname not filled!\n";
validationVerified=false;
}
if (registerForm.login.value=="")
{
errorMessage+="Email not filled!\n";
validationVerified=false;
}
if(registerForm.password.value=="")
{
errorMessage+="Password not provided!\n";
validationVerified=false;
}
if(registerForm.cpassword.value=="")
{
errorMessage+="Confirm password not filled!\n";
validationVerified=false;
}
if(registerForm.cpassword.value!=registerForm.password.value)
{
errorMessage+="Password and Confirm Password do not match!\n";
validationVerified=false;
}
if (!isValidEmail(registerForm.login.value)) {
errorMessage+="Invalid email address provided!\n";
validationVerified=false;
}
if(registerForm.question.selectedIndex==0)
{
errorMessage+="Question not selected!\n";
validationVerified=false;
}
if(registerForm.answer.value=="")
{
errorMessage+="Answer not filled!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate email function
function isValidEmail(val) {
	var re = /^[\w\+\'\.-]+@[\w\'\.-]+\.[a-zA-Z]{2,}$/;
	if (!re.test(val)) {
		return false;
	}
    return true;
}
 
//validate special PIN
function isValidSpecialPIN(val) {
	var re = /^[0-9][0-9][A-Z][A-Z][A-Z][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$/;
	if (!re.test(val)) {
		return false;
	}
	return true;
}
 
//validate special PIN length
function isValidLength(val){
	var length = 12;
	if (!re.test(val)) {
		return false;
	}
	return true;
}
 
//function to handle passwordResetForm validation
function passwordResetValidate(resetForm){
 
var validationVerified=true;
var errorMessage="";
 
if (resetForm.email.value=="")
{
errorMessage+="Please enter your account email! We need your email in order to reset your password.\n";
validationVerified=false;
}
if (!isValidEmail(resetForm.email.value)) {
errorMessage+="Invalid email address provided!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//function to handle passwordResetForm validation(2)
function passwordResetValidate_2(resetForm){
 
var validationVerified=true;
var errorMessage="";
 
if (resetForm.answer.value==""){
errorMessage+="Please enter your security answer to your provided security question.\n";
validationVerified=false;
}
if (resetForm.new_password.value==""){
errorMessage+="New Password not set!\n";
validationVerified=false;
}
if (resetForm.confirm_new_password.value==""){
errorMessage+="Confirm New Password not set!\n";
validationVerified=false;
}
if (resetForm.new_password.value!=resetForm.confirm_new_password.value){
errorMessage+="New Password and Confirm New Password do not match!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
// onchange of qty field entry totals the price
function getProductTotal(field) {
    clearErrorInfo();
    var form = field.form;
	if (field.value == "") field.value = 0;
	if ( !isPosInt(field.value) ) {
        var msg = 'Please enter a positive integer for quantity.';
        addValidationMessage(msg);
        addValidationField(field)
        displayErrorInfo( form );
        return;
	} else {
		var product = field.name.slice(0, field.name.lastIndexOf("_") ); 
        var price = form.elements[product + "_price"].value;
		var amt = field.value * price;
		form.elements[product + "_tot"].value = formatDecimal(amt);
		doTotals(form);
	}
}
 
function doTotals(form) {
    var total = 0;
    for (var i=0; PRODUCT_ABBRS[i]; i++) {
        var cur_field = form.elements[ PRODUCT_ABBRS[i] + "_qty" ]; 
        if ( !isPosInt(cur_field.value) ) {
            var msg = 'Please enter a positive integer for quantity.';
            addValidationMessage(msg);
            addValidationField(cur_field)
            displayErrorInfo( form );
            return;
        }
        total += parseFloat(cur_field.value) * parseFloat( form.elements[ PRODUCT_ABBRS[i] + "_price" ].value );
    }
    form.elements['total'].value = formatDecimal(total);
}
 
//validate orderform
function finalCheck(orderForm) {
    var validationVerified=true;
var errorMessage="";
 
if (orderForm.quantity.value=="")
{
errorMessage+="Please provide a quantity.\n";
validationVerified=false;
}
if (orderForm.quantity.value==0)
{
errorMessage+="Please provide a quantity rather than 0.\n";
validationVerified=false;
}
if(orderForm.total.value=="")
{
errorMessage+="Total has not been calculated! Please provide first the quantity.\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate updateForm
function updateValidate(updateForm) {
    var validationVerified=true;
var errorMessage="";
 
if (updateForm.opassword.value=="")
{
errorMessage+="Please provide your old password.\n";
validationVerified=false;
}
if (updateForm.npassword.value=="")
{
errorMessage+="Please provide a new password.\n";
validationVerified=false;
}
if(updateForm.cpassword.value=="")
{
errorMessage+="Please confirm your new password.\n";
validationVerified=false;
}
if(updateForm.cpassword.value!=updateForm.npassword.value)
{
errorMessage+="Confirm Password and New Password do not match!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate billingForm
function billingValidate(billingForm) {
    var validationVerified=true;
var errorMessage="";
 
if (billingForm.sAddress.value=="")
{
errorMessage+="Please provide a street address.\n";
validationVerified=false;
}
if (billingForm.box.value=="")
{
errorMessage+="Please provide your postal box number.\n";
validationVerified=false;
}
if (billingForm.city.value=="")
{
errorMessage+="Please provide your city.\n";
validationVerified=false;
}
if(billingForm.mNumber.value=="")
{
errorMessage+="Please provide your mobile number.\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate table form
function tableValidate(tableForm){
 
var validationVerified=true;
var errorMessage="";
 
if (tableForm.table.selectedIndex==0)
{
errorMessage+="Please select a table by its name or number.\n";
validationVerified=false;
}
if (tableForm.date.value=="")
{
errorMessage+="Please provide a reservation date.\n";
validationVerified=false;
}
if (tableForm.time.value=="")
{
errorMessage+="Please provide a reservation time.\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate partyhall form
function partyhallValidate(partyhallForm){
 
var validationVerified=true;
var errorMessage="";
 
if (partyhallForm.partyhall.selectedIndex==0)
{
errorMessage+="Please select a partyhall by its name or number.\n";
validationVerified=false;
}
if (partyhallForm.date.value=="")
{
errorMessage+="Please provide a reservation date.\n";
validationVerified=false;
}
if (partyhallForm.time.value=="")
{
errorMessage+="Please provide a reservation time.\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate categories form
function categoriesValidate(categoriesForm){
 
var validationVerified=true;
var errorMessage="";
 
if (categoriesForm.category.selectedIndex==0)
{
errorMessage+="Please select a category first!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate quantity form
function updateQuantity(quantityForm){
 
var validationVerified=true;
var errorMessage="";
 
if (quantityForm.item.selectedIndex==0)
{
errorMessage+="Please select an item id first!\n";
validationVerified=false;
}
if (quantityForm.quantity.selectedIndex==0)
{
errorMessage+="Please select a quantity first!\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//validate rating form
function ratingValidate(ratingForm){
 
var validationVerified=true;
var errorMessage="";
 
if (ratingForm.food.selectedIndex==0)
{
errorMessage+="Please select the food. This information is necessary in order to serve you better.\n";
validationVerified=false;
}
if (ratingForm.scale.selectedIndex==0)
{
errorMessage+="Please select the scale. This information is necessary in order to serve you better.\n";
validationVerified=false;
}
if(!validationVerified)
{
alert(errorMessage);
}
return validationVerified;
}
 
//reset password popup
 function resetPassword()
 {
window.open('password-reset.php','resetPassword','toolbar=no,location=no,directories=no,status=no,menubar=no,resizable=no,copyhistory=no,scrollbars=yes,width=480,height=320');
 }
 
 //validates quantity and redirects quantity to update-quantity.php
function getQuantity(int)
{
    if (window.XMLHttpRequest)
      {// code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttp=new XMLHttpRequest();
      }
    else
      {// code for IE6, IE5
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
 
    xmlhttp.open("GET","update-quantity.php?quantity_id="+int,true);
    xmlhttp.send();
}
 
//live clock function
function updateClock ( )
{
  var currentTime = new Date ( );
 
  var currentHours = currentTime.getHours ( );
  var currentMinutes = currentTime.getMinutes ( );
  var currentSeconds = currentTime.getSeconds ( );
 
  // Pad the minutes and seconds with leading zeros, if required
  currentMinutes = ( currentMinutes < 10 ? "0" : "" ) + currentMinutes;
  currentSeconds = ( currentSeconds < 10 ? "0" : "" ) + currentSeconds;
 
  // Choose either "AM" or "PM" as appropriate
  var timeOfDay = ( currentHours < 12 ) ? "AM" : "PM";
 
  // Convert the hours component to 12-hour format if needed
  currentHours = ( currentHours > 12 ) ? currentHours - 12 : currentHours;
 
  // Convert an hours component of "0" to "12"
  currentHours = ( currentHours == 0 ) ? 12 : currentHours;
 
  // Compose the string for display
  var currentTimeString = currentHours + ":" + currentMinutes + ":" + currentSeconds + " " + timeOfDay;
 
  // Update the time display
  document.getElementById("clock").innerHTML = currentTimeString;
} 

The user.js file indeed handles some functions on the client-side.

I am simply disable it

Authentication Bypass


Authentication is made through sending a POST request to the /rms/login-exec.php endpoint

Interestingly, I can bypass the entire authentication process if I send a GET request to the /rms/login-exec.php endpoint

Fuzzing /rms/


┌──(kali㉿kali)-[~/archive/thm/zeno]
└─$ ffuf -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -t 200 -u http://$IP:12340/rms/FUZZ -ic
________________________________________________
 :: Method           : GET
 :: URL              : http://10.10.20.195:12340/rms/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 200
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
.htaccess               [Status: 403, Size: 215, Words: 15, Lines: 9, Duration: 2090ms]
.htpasswd               [Status: 403, Size: 215, Words: 15, Lines: 9, Duration: 3441ms]
admin                   [Status: 301, Size: 244, Words: 14, Lines: 8, Duration: 189ms]
connection              [Status: 301, Size: 249, Words: 14, Lines: 8, Duration: 164ms]
css                     [Status: 301, Size: 242, Words: 14, Lines: 8, Duration: 80ms]
fonts                   [Status: 301, Size: 244, Words: 14, Lines: 8, Duration: 237ms]
images                  [Status: 301, Size: 245, Words: 14, Lines: 8, Duration: 160ms]
stylesheets             [Status: 301, Size: 250, Words: 14, Lines: 8, Duration: 150ms]
swf                     [Status: 301, Size: 242, Words: 14, Lines: 8, Duration: 81ms]
validation              [Status: 301, Size: 249, Words: 14, Lines: 8, Duration: 41ms]
:: Progress: [20476/20476] :: Job [1/1] :: 340 req/sec :: Duration: [0:01:04] :: Errors: 86 ::

ffuf found some interesting endpoints Those appear to lead to nowhere

Vulnerability


┌──(kali㉿kali)-[~/archive/thm/zeno]
└─$ searchsploit restaurant management system
------------------------------------------------------------- ---------------------------------
 Exploit Title                                               |  Path
------------------------------------------------------------- ---------------------------------
Restaurant Management System 1.0  - SQL Injection            | php/webapps/51330.txt
Restaurant Management System 1.0 - Remote Code Execution     | php/webapps/47520.py
------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

Searching Restaurant Management System for vulnerability reveals a RCE vulnerability