View Issue Details

IDProjectCategoryView StatusLast Update
0000301XMB1Bugspublic2010-01-23 18:18
ReporterJohn Briggs Assigned Tomiqrogroove  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Product Version1.9.8 SP2 
Target Version1.9.11.05Fixed in Version1.9.11.05 
Summary0000301: Last Super Admin Self-Demotion
DescriptionThis is a basic security issue. Being able to demote yourself or having another super admin demote another who is the primary super admin is abig no no within the logic.
Steps To ReproduceGo to editprofile.php and edit your own account as super admin. Select admin or other status and it will permit you to demote yourself. It will also allow any other super admins to demote themselves or you. This is a logic security issue and should be considered major. Great efforts were made within the admin panel to prevent this type of behavior and now editprofile allows it with ease.
TagsNo tags attached.
MySQL Version
PHP Version
Web Server
Browser
Flags
Original Reporter
SVN Revision1814

Activities

miqrogroove

2009-03-16 10:34

administrator   ~0000168

It looks like I made a mistake at revision 1184 that changed the last-sa protection to check for deletes but not edits.

However, after going through this with a fine-tooth comb, I cannot find any code that would have prevented a "super admin demote another who is the primary super admin". Demotion has always been allowed when there is more than one registered sadmin.

2009-03-16 11:28

 

XMB-1.9.11-last-sadmin-demotion.patch.txt (3,871 bytes)   
Index: cp.php
===================================================================
--- cp.php	(revision 1745)
+++ cp.php	(working copy)
@@ -1728,9 +1728,15 @@
                 } else {
                     $pending = '';
                 }
+                
+                if ($member['status'] == 'Super Administrator') {
+                    $disabledelete = ' disabled="disabled"';
+                } else {
+                    $disabledelete = '';
+                }
                 ?>
                 <tr bgcolor="<?php echo $altbg2?>" class="tablerow">
-                <td align="center"><input type="checkbox" name="delete<?php echo $member['uid']?>" onclick="addUserDel(<?php echo $member['uid']?>, '<?php echo $member['username']?>', this)" value="<?php echo $member['uid']?>" /></td>
+                <td align="center"><input type="checkbox" name="delete<?php echo $member['uid']?>" onclick="addUserDel(<?php echo $member['uid']?>, '<?php echo $member['username']?>', this)" value="<?php echo $member['uid']?>"<?php echo $disabledelete; ?> /></td>
                 <td><a href="member.php?action=viewpro&amp;member=<?php echo recodeOut($member['username']); ?>"><?php echo $member['username']?></a>
                 <?php if (X_SADMIN) { ?>
                 <br /><a href="editprofile.php?user=<?php echo recodeOut($member['username']); ?>"><strong><?php echo $lang['admin_edituseraccount']; ?></strong></a>
@@ -1777,8 +1783,8 @@
             <?php
         }
     } else if (onSubmit('membersubmit')) {
-        $query = $db->query("SELECT MIN(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
-        $sa_uid = $db->result($query, 0);
+        $query = $db->query("SELECT COUNT(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
+        $sa_count = $db->result($query, 0);
         $db->free_result($query);
 
         $query = $db->query("SELECT uid, username, password, status FROM ".X_PREFIX."members $where");
@@ -1793,6 +1799,9 @@
             if (!X_SADMIN && ($origstatus == "Super Administrator" || $status == "Super Administrator")) {
                 continue;
             }
+            if ($origstatus == 'Super Administrator' And $status != 'Super Administrator' And $sa_count == 1) {
+                error($lang['lastsadmin'], false, '</td></tr></table></td></tr></table><br />');
+            }
 
             $banstatus = postedVar('banstatus'.$mem['uid']);
             $cusstatus = postedVar('cusstatus'.$mem['uid'], '', FALSE);
@@ -1807,7 +1816,7 @@
                 }
             }
 
-            if ($delete == $mem['uid'] && $delete != $self['uid'] && $delete != $sa_uid) {
+            if ($delete == $mem['uid'] && $delete != $self['uid'] && $origstatus != "Super Administrator") {
                 $dbname = $db->escape_var($mem['username']);
                 $db->query("DELETE FROM ".X_PREFIX."members WHERE uid=$delete");
                 $db->query("DELETE FROM ".X_PREFIX."buddys WHERE username='$dbname'");
Index: editprofile.php
===================================================================
--- editprofile.php	(revision 1745)
+++ editprofile.php	(working copy)
@@ -226,6 +226,13 @@
     eval('$editpage = "'.template('admintool_editprofile').'";');
 } else {
     $status = postedVar('status');
+    $origstatus = $member['status'];
+    $query = $db->query("SELECT COUNT(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
+    $sa_count = $db->result($query, 0);
+    $db->free_result($query);
+    if ($origstatus == 'Super Administrator' And $status != 'Super Administrator' And $sa_count == 1) {
+        error($lang['lastsadmin']);
+    }
     $cusstatus = postedVar('cusstatus', '', FALSE);
     $langfilenew = postedVar('langfilenew');
     $result = $db->query("SELECT devname FROM ".X_PREFIX."lang_base WHERE devname='$langfilenew'");

miqrogroove

2009-03-16 11:30

administrator   ~0000169

Attached patch file restores last-sa edit protection, removes ambiguous "primary super admin" code, and adds delete protection for all sadmins (must demote to delete).

I would like your feedback before committing this.

John Briggs

2009-03-16 17:42

reporter   ~0000173

Looks fine. Apparently at some point the code that prevented demotion in cp was lost or coded over as it did exist at a point before 1.9.11 or even 10. I will test the above patch and return with feedback. Some of the legacy code does indeed exist in the current base code from what I see as well. 1.9.8 may be a good example to look for the older code for prevention.

miqrogroove

2009-03-16 17:50

administrator   ~0000174

The way I'm reading 1.9.8, it prevents Admins from editing Super Admins, and prevents last-sa status changes. When 2 or more Supers are registered they would be able to edit each other.

John Briggs

2009-03-16 23:04

reporter   ~0000176

ah, then that's agood thing I caught it. You are correct.

John Briggs

2009-03-16 23:15

reporter   ~0000177

Code approved and tested. It serves it's function. Thanks Rob.

miqrogroove

2009-03-17 11:40

administrator   ~0000178

As soon as I committed the patch, I realized what MIN(uid) was being used for. It was a guarantee that at least one sadmin would exist after a mass demotion. This does need more work.

miqrogroove

2009-03-17 11:52

administrator   ~0000179

... And after reading 1.9.8 for the third time, it looks like MIN(uid) was never implemented properly. It was added in 1.9.4 and remained unchanged through 1.9.11. This now appears to be a legacy bug.

2009-03-17 12:47

 

XMB-1.9.11-last-sadmin-part2.diff (2,049 bytes)   
Index: cp.php
===================================================================
--- cp.php	(revision 1808)
+++ cp.php	(working copy)
@@ -1783,12 +1783,26 @@
             <?php
         }
     } else if (onSubmit('membersubmit')) {
-        $query = $db->query("SELECT COUNT(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
-        $sa_count = $db->result($query, 0);
-        $db->free_result($query);
-
         $query = $db->query("SELECT uid, username, password, status FROM ".X_PREFIX."members $where");
 
+        // Guarantee this request will not remove all Super Administrators.
+        if (X_SADMIN And $db->num_rows($query) > 0) {
+            $saquery = $db->query("SELECT COUNT(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
+            $sa_count = $db->result($saquery, 0);
+            $db->free_result($saquery);
+
+            while($mem = $db->fetch_array($query)) {
+                if ($mem['status'] == 'Super Administrator' And postedVar('status'.$mem['uid']) != 'Super Administrator') {
+                    $sa_count--;
+                }
+            }
+            if ($sa_count < 1) {
+                error($lang['lastsadmin'], false, '</td></tr></table></td></tr></table><br />');
+            }
+            $db->data_seek($query, 0);
+        }
+
+        // Now execute this request
         while($mem = $db->fetch_array($query)) {
             $origstatus = $mem['status'];
             $status = postedVar('status'.$mem['uid']);
@@ -1799,9 +1813,6 @@
             if (!X_SADMIN && ($origstatus == "Super Administrator" || $status == "Super Administrator")) {
                 continue;
             }
-            if ($origstatus == 'Super Administrator' And $status != 'Super Administrator' And $sa_count == 1) {
-                error($lang['lastsadmin'], false, '</td></tr></table></td></tr></table><br />');
-            }
 
             $banstatus = postedVar('banstatus'.$mem['uid']);
             $cusstatus = postedVar('cusstatus'.$mem['uid'], '', FALSE);

2009-03-17 12:50

 

XMB-1.9.11-last-sadmin-v2.patch.txt (4,159 bytes)   
Index: cp.php
===================================================================
--- cp.php	(revision 1806)
+++ cp.php	(working copy)
@@ -1728,9 +1728,15 @@
                 } else {
                     $pending = '';
                 }
+                
+                if ($member['status'] == 'Super Administrator') {
+                    $disabledelete = ' disabled="disabled"';
+                } else {
+                    $disabledelete = '';
+                }
                 ?>
                 <tr bgcolor="<?php echo $altbg2?>" class="tablerow">
-                <td align="center"><input type="checkbox" name="delete<?php echo $member['uid']?>" onclick="addUserDel(<?php echo $member['uid']?>, '<?php echo $member['username']?>', this)" value="<?php echo $member['uid']?>" /></td>
+                <td align="center"><input type="checkbox" name="delete<?php echo $member['uid']?>" onclick="addUserDel(<?php echo $member['uid']?>, '<?php echo $member['username']?>', this)" value="<?php echo $member['uid']?>"<?php echo $disabledelete; ?> /></td>
                 <td><a href="member.php?action=viewpro&amp;member=<?php echo recodeOut($member['username']); ?>"><?php echo $member['username']?></a>
                 <?php if (X_SADMIN) { ?>
                 <br /><a href="editprofile.php?user=<?php echo recodeOut($member['username']); ?>"><strong><?php echo $lang['admin_edituseraccount']; ?></strong></a>
@@ -1777,12 +1783,26 @@
             <?php
         }
     } else if (onSubmit('membersubmit')) {
-        $query = $db->query("SELECT MIN(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
-        $sa_uid = $db->result($query, 0);
-        $db->free_result($query);
-
         $query = $db->query("SELECT uid, username, password, status FROM ".X_PREFIX."members $where");
 
+        // Guarantee this request will not remove all Super Administrators.
+        if (X_SADMIN And $db->num_rows($query) > 0) {
+            $saquery = $db->query("SELECT COUNT(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
+            $sa_count = $db->result($saquery, 0);
+            $db->free_result($saquery);
+
+            while($mem = $db->fetch_array($query)) {
+                if ($mem['status'] == 'Super Administrator' And postedVar('status'.$mem['uid']) != 'Super Administrator') {
+                    $sa_count--;
+                }
+            }
+            if ($sa_count < 1) {
+                error($lang['lastsadmin'], false, '</td></tr></table></td></tr></table><br />');
+            }
+            $db->data_seek($query, 0);
+        }
+
+        // Now execute this request
         while($mem = $db->fetch_array($query)) {
             $origstatus = $mem['status'];
             $status = postedVar('status'.$mem['uid']);
@@ -1807,7 +1827,7 @@
                 }
             }
 
-            if ($delete == $mem['uid'] && $delete != $self['uid'] && $delete != $sa_uid) {
+            if ($delete == $mem['uid'] && $delete != $self['uid'] && $origstatus != "Super Administrator") {
                 $dbname = $db->escape_var($mem['username']);
                 $db->query("DELETE FROM ".X_PREFIX."members WHERE uid=$delete");
                 $db->query("DELETE FROM ".X_PREFIX."buddys WHERE username='$dbname'");
Index: editprofile.php
===================================================================
--- editprofile.php	(revision 1806)
+++ editprofile.php	(working copy)
@@ -226,6 +226,13 @@
     eval('$editpage = "'.template('admintool_editprofile').'";');
 } else {
     $status = postedVar('status');
+    $origstatus = $member['status'];
+    $query = $db->query("SELECT COUNT(uid) FROM ".X_PREFIX."members WHERE status='Super Administrator'");
+    $sa_count = $db->result($query, 0);
+    $db->free_result($query);
+    if ($origstatus == 'Super Administrator' And $status != 'Super Administrator' And $sa_count == 1) {
+        error($lang['lastsadmin']);
+    }
     $cusstatus = postedVar('cusstatus', '', FALSE);
     $langfilenew = postedVar('langfilenew');
     $result = $db->query("SELECT devname FROM ".X_PREFIX."lang_base WHERE devname='$langfilenew'");

miqrogroove

2009-03-17 12:53

administrator   ~0000180

New diff and revised full patch attached, to be committed tomorrow.

John Briggs

2009-03-17 16:13

reporter   ~0000182

Glad you found it. THanks Rob.

Issue History

Date Modified Username Field Change
2009-03-15 13:19 John Briggs New Issue
2009-03-16 10:10 miqrogroove Status new => acknowledged
2009-03-16 10:34 miqrogroove Note Added: 0000168
2009-03-16 10:34 miqrogroove Status acknowledged => confirmed
2009-03-16 10:38 miqrogroove Assigned To => miqrogroove
2009-03-16 10:38 miqrogroove Status confirmed => assigned
2009-03-16 10:38 miqrogroove Projection none => tweak
2009-03-16 10:38 miqrogroove ETA none => < 1 day
2009-03-16 10:38 miqrogroove Product Version 1.9.11.04 => 1.9.11
2009-03-16 10:38 miqrogroove Target Version => 1.9.11.05
2009-03-16 10:38 miqrogroove Summary Editprofile Rank CHange Issue => Last Super Admin Self-Demotion
2009-03-16 11:28 miqrogroove File Added: XMB-1.9.11-last-sadmin-demotion.patch.txt
2009-03-16 11:30 miqrogroove Note Added: 0000169
2009-03-16 17:42 John Briggs Note Added: 0000173
2009-03-16 17:50 miqrogroove Note Added: 0000174
2009-03-16 23:04 John Briggs Note Added: 0000176
2009-03-16 23:15 John Briggs Note Added: 0000177
2009-03-17 11:29 miqrogroove SVN Revision => 1808
2009-03-17 11:29 miqrogroove Status assigned => resolved
2009-03-17 11:29 miqrogroove Fixed in Version => 1.9.11.05
2009-03-17 11:29 miqrogroove Resolution open => fixed
2009-03-17 11:40 miqrogroove Note Added: 0000178
2009-03-17 11:40 miqrogroove Status resolved => feedback
2009-03-17 11:40 miqrogroove Resolution fixed => reopened
2009-03-17 11:52 miqrogroove Note Added: 0000179
2009-03-17 11:52 miqrogroove Product Version 1.9.11 => 1.9.8 SP2
2009-03-17 11:52 miqrogroove Fixed in Version 1.9.11.05 =>
2009-03-17 12:47 miqrogroove File Added: XMB-1.9.11-last-sadmin-part2.diff
2009-03-17 12:50 miqrogroove File Added: XMB-1.9.11-last-sadmin-v2.patch.txt
2009-03-17 12:53 miqrogroove Note Added: 0000180
2009-03-17 12:53 miqrogroove Status feedback => assigned
2009-03-17 16:13 John Briggs Note Added: 0000182
2009-03-18 10:40 miqrogroove SVN Revision 1808 => 1814
2009-03-18 10:40 miqrogroove Status assigned => resolved
2009-03-18 10:40 miqrogroove Fixed in Version => 1.9.11.05
2009-03-18 10:40 miqrogroove Resolution reopened => fixed
2010-01-23 18:18 miqrogroove Status resolved => closed