SOLV-DB - Session Takeover (#mvid3)

Document Title:
===============
SOLV-DB - Session Takeover


mosi Vulnerability ID (mvid):
===============
3


Discovery Status:
=============
No Fix


CVSSv2 Overall Score:
===============
2.4


CVSSv2 Vector:
==============
(AV:N/AC:M/Au:M/C:N/I:P/A:N/E:POC/RL:U/RC:C/CDP:L/TD:H/CR:L/IR:L/AR:H)
https://nvd.nist.gov/cvss.cfm?calculator&version=2&vector=(AV:N/AC:M/Au:M/C:N/I:P/A:N/E:POC/RL:U/RC:C/CDP:L/TD:H/CR:L/IR:L/AR:H)


Product & Service Introduction:
==============
The SOLV-DB is a central runner database used for simplifying the organisation and registration of runners for orienteering competitions in Switzerland and is provided by the Swiss Orienteering federation.
Every runner has it's own runner ID, which is central for the registration on events.


Abstract:
==============
Simon Monai found a vulnerability in the database form, so he could take over a session of another user.


Report Timeline:
==============
2016-11-29 - Vendor Informed
2016-12-13 - Vendor reminder
2016-12-13 - Vendor acknowledgement
2016-12-13 - Vendor will not fix vulnerability
2016-12-18 - Public Disclosure


Affected Products:
=============
Swiss Orienteering Runner's Database - Online Form


Exploitation Technique:
=============
HTTP Form Manipulation (Remote)


Security Level:
=============
Low


Technical Details & Description:
=============
Request method (s):
[+] GET (Possible, not used)
[+] POST

Vulnerable Module(s):
[+] http://www.o-l.ch/cqi-bin/solvdb

Vulnerable File(s):
[+] (None)

Vulnerable Parameter(s):
[+] sessid


Proof of Concept (PoC):
=============
After login with the user credential on http://www.o-l.ch/cgi-bin/solvdb (nothing but a POST form with the SOLVID and other predefined information) following (simplified) form is loaded:

<form name="competitor" method="POST" action="/cgi-bin/solvdb">
<table width="800" border="0" cellspacing="0" cellpadding="1" bgcolor="#eeeeee">
<tbody>
<tr>
<td>SOLV-Nr:</td>
<td>
<input type="text" size="9" name="solvnr" value="AA1BBB" onfocus="setTimeout('document.competitor.solvnr.blur()',1);">
</td>
<td>SI-Card:</td>
<td>
<input type="text" size="8" maxlength="8" name="sinr" value="1234567">
</td>
<td>IOF Athlete ID:</td>
<td>
<input type="text" size="8" maxlength="8" name="iofid" value="0">
&nbsp;&nbsp;
</td>
</tr>
<tr>
<td>Geschl.:</td>
<td>
<select name="sex" size="1">
<option value="-">-</option>
<option selected="selected" value="M">M</option>
<option value="F">F</option>
</select>
</td>
<td>Vorname:</td>
<td>
<input type="text" size="28" name="fname" value="Hans">
</td>
<td>Nachname:</td>
<td>
<input type="text" size="28" name="lname" value="Muster">
</td>
</tr>
<tr>
<td>Geb.jahr:</td>
<td>
<input type="text" size="5" name="yob" value="1990">
</td>
<td>Address1:</td>
<td>
<input type="text" size="28" name="addr1" value="Musterweg 1">
</td>
<td>Address2:</td>
<td>
<input type="text" size="28" name="addr2" value="">
</td>
</tr>
<tr>
<td>Land:</td>
<td>
<select name="rescntry" size="1">
<option value="-">-</option>
<option selected="selected" value="CH">CH</option>
<option value="AT">AT</option>
<option value="DE">DE</option>
<option value="FR">FR</option>
<option value="IT">IT</option>
</select>
</td>
<td>
PLZ/
<a href="javascript:chooseWindow('/cgi-bin/solvdb?&amp;sessid=725gQBBZBOp4I'+'&amp;choose=1&amp;plz=0')">
Ort
</a>
:
</td>
<td>
<input type="text" size="5" name="plz" value="0000" onchange="changePlz(this.form)" onfocus="checkCountry();">
<input type="text" size="22" name="city" value="Musterdorf" onfocus="checkCountry();">
</td>
<td>Kanton:</td>
<td>
<input type="hidden" name="kt" value="ZH">
TG&nbsp;
Region:&nbsp;
<input type="hidden" name="rg" value="ZH">
OS
</td>
</tr>
<tr>
<td>Kategorie:</td>
<td>
<select name="categ" size="1">
<option value="-">-</option>
<option value="DE">DE</option>
<option value="DAL">DAL</option>
<option value="DAM">DAM</option>
<option value="DAK">DAK</option>
<option value="DB">DB</option>
<option value="HE">HE</option>
<option value="HAL">HAL</option>
<option selected="selected" value="HAM">HAM</option>
<option value="HAK">HAK</option>
<option value="HB">HB</option>
<option value="OL">OL</option>
<option value="OM">OM</option>
<option value="OK">OK</option>
</select>
</td>
<td>Club:</td>
<td>
<select name="club" size="1">
<option value="-">-</option>
<option value="clubname1">clubname1</option>
<option value="clubname2">clubname2</option>
<option value="clubname2">clubname3</option>
<option value="clubname2">clubname4</option>
</select>
</td>
<td>Nation:</td>
<td>
<select name="cntsh" size="1">
<option value="-">-</option>
<option selected="selected" value="SUI">SUI</option>
<option value="AUT">AUT</option>
<option value="FRA">FRA</option>
<option value="GER">GER</option>
<option value="ITA">ITA</option>
<option value="LIE">LIE</option>
</select>
</td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
<td>Dop. Stat:</td>
<td>Ja, 2016-01-22
<input type="hidden" name="dopstat" value="1">
<input type="hidden" name="dopstatdate" value="2016-01-22">
</td>
<td>E-Mail:</td>
<td>
<input type="text" size="28" name="email" value="Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!">
</td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
<td>ID=11111
<input type="hidden" name="id" value="11111">
</td>
<td>&nbsp;</td>
<td>Mobile-Nummer:</td>
<td><input type="text" size="16" maxlength="16" name="mobile" value="0761234567"></td>
</tr>

<input type="hidden" name="subsc" value="0">
<input type="hidden" name="flags" value="0">
<input type="hidden" name="rem" value="">

<tr bgcolor="#cccccc">
<td colspan="3">
<input type="hidden" name="user" value="1">
<input type="submit" value="&nbsp;Submit&nbsp;">
<input type="reset" value="Reset">
</td>
<td>Creation: 2010-01-01</td>
<td>LastMod:</td>
<td>
2015-01-15 09:33:08 laubeh&nbsp;&nbsp;
<a href="http://map.search.ch/0000-Musterdorf/Musterweg%201">map</a>
</td>
</tr>

<input type="hidden" name="competitor" value="modi">
<input type="hidden" name="letter" value="M">
</tbody>
</table>

<input type="hidden" name="sessid" value="725gQBBZBOp4I">
</form>

Plase note the last line. There is an hidden input with the session ID. If another another user loggs in, he gets a new unique ID. With a Man-in-the-Middle-Attack (MIM-Attack) or by just knowing another solv id (for example as it was possible with #mvid1), I was able to gather another sessid.
By replacing this value (edit site's source code with the developer tools), the changes could be submitted successfully. By reloading the runners entry page, the form showing the changes and a notification to wait for approval will be loaded (simplified version):

<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td><img src="/images/pix.gif" alt="" height="1" width="10"></td>
<td valign="top">
<h2>User Änderung</h2>
<p>
Änderungen können hier durch die Läufer(innen) selbst eingegeben werden.
<br>Sie werden erst nach der Überprüfung durch den Administrator definitiv in die DB aufgenommen.
<br>Für die Online-Anmeldung werden sie aber sofort wirksam.
<br>Ausnahme: für Familien- und Clubanmeldungen müssen die Änderungen erst vom Administrator bestätigt werden.
</p>
<script language="JavaScript">
<!--
var obj='';
var text='';
var text2='';
var text3='';
var text4='';
function restart() {
if(obj != '') document.competitor.city.value = obj;
if(text != '') document.competitor.rg.value = text;
if(text2 != '') document.competitor.kt.value = text2;
if(text3 != '') document.competitor.rescntry.value = text3;
if(text4 != '') document.competitor.plz.value = text4;
mywindow.close();
}
function chooseWindow(url) {
mywindow=open(url,'myname','resizable=yes,scrollbars=yes,width=600,height=550');
mywindow.location.href = url;
if (mywindow.opener == null) mywindow.opener = self;
}
var ctype='Textblock';
function changePlz(object) {
var plz = object.plz.value;
if(document.competitor.rescntry.value == 'CH') chooseWindow('/cgi-bin/solvdb?&sessid=ZD4OvIDI/LZRU'+'&choose=1&plz=' + plz);
}
function checkCountry(object) {
if(document.competitor.rescntry.value == '-')
{
alert('Zuerst "Land" auswählen!');
document.competitor.plz.blur();
document.competitor.city.blur();
}
}
//-->
</script>
<form name="competitor" method="POST" action="/cgi-bin/solvdb">
<table width="800" border="0" cellspacing="0" cellpadding="1" bgcolor="#eeeeee">

[ORIGINAL RUNNER INFORMATION]

</table>
<input type="hidden" name="sessid" value="ZD4OvIDI/LZRU">
</form>
<p>
<font color="#ff0000">
Es ist eine Änderung hängig, die erst vom Administrator approved werden muss.
<br>Solange Änderungen hängig sind, können keine weiteren User Änderungen gemacht werden.
</font>
</p>
<p>
<b>Unapproved Update:</b>
</p>
<form name="competitor_ne" method="POST" action="/cgi-bin/solvdb">
<table width="800" border="0" cellspacing="0" cellpadding="1" bgcolor="#eeeeee">
<tbody>

[UPDTAED RUNNER INFORMATION]

<input type="hidden" name="subsc" value="0"><input type="hidden" name="flags" value="0"><input type="hidden" name="rem" value="">
<tr bgcolor="#cccccc">
<td colspan="3">&nbsp;</td>
<td>Creation: 2010-01-01</td>
<td>LastMod:</td>
<td>
2016-11-25 10:34:44 user-LO5GEF&nbsp;&nbsp;
<a href="http://map.search.ch/0000-Musterdorf/Musterweg%201">map</a>
</td>
</tr>
<input type="hidden" name="competitor" value="modi">
<input type="hidden" name="letter" value="M">
</tbody>
</table>
<input type="hidden" name="sessid" value="ZD4OvIDI/LZRU">
</form>
<p>
Zurück zum <a href="/cgi-bin/solvdb">Start Läufer DB</a>.
</p>
<br>
<table border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td>
<b>Disclaimer</b>
<ul>
<li>Die Swiss Orienteering Läufer Datenbank wird nur innerhalb von Swiss Orienteering und im Interesse des OLs verwendet.</li>
<li>Wer wünscht, dass seine Daten in keinem Fall für andere Zwecke als für Wettkämpfe verwendet werden, kann sich in eine spezielle Liste eintragen lassen. Auch wer ganz aus der Datenbank gelöscht werden will, kann dies verlangen.</li>
<li>OL Veranstalter erhalten eine Kopie der Swiss Orienteering Läufer Datenbank, die ausschliesslich für die Organisation des Wettkampfes verwendet werden darf. Anfragen dazu sind direkt an die Geschäftsstelle von Swiss Orienteering (info _AT_ swiss-orienteering.ch) zu richten.</li>
<li>Die Email Adresse wird nur intern verwendet (etwa im Fall von Unklarheiten) und nicht weitergegeben.</li>
</ul>
</td>
</tr>
</tbody>
</table>
</td>
<td>
<img src="/images/pix.gif" alt="" height="1" width="10">
</td>
</tr>
</tbody>
</table>

As it is visible, the changes have been saved, but the user changing the runners information is not user-EU6MOS but in this case (see line 'LastMod:') user-LO5GEF.


Possible Solution:
============
The session should not be written directly into the the form but be managed with the built-in PHP-Session options (I would like to refer to the $_SESSION variable in PHP).
Further, HTTPS should be enabled on the webserver to prevent that other network user can sniff the runner's information.

Security Risk:
============
This vulnerability is estimated as a low security risk (CVSSv2 2.4).


Author / Credits:
============
mosi security research - Simon Monai (http://jongliertricks.ch/kontakt)


Public Disclosure:
============
2016-12-18 - https://jongliertricks.ch/mosi-security-research/36

----------------------------
https://jongliertricks.ch/mosi-security-research