Linuchs: Über 1800 Aufrufe meiner Webseite Hacker-Versuch?

Moin,

wenn ein Server-Programm länger als 3 sec läuft, bekomme ich eine Mail.

Heute morgen 5:28 und 5:29 kamen 211 Aufrufe in dieser Form:

/?TID=17540+%2F%2A%2A%2F%27%2F%2A%2A%2FOR%2F%2A%2A%2F1%2F%2A%2A%2FGROUP%2F%2A%2A%2FBY%2F%2A%2A%2FCONCAT%280x6167754e%2C%28SELECT%2F%2A%2A%2FMID%28IFNULL%28USER%28%29%2C%2F%2A%2A%2F0x20%29%2C1%2C55%29%29%2C0x7241306a%2CFLOOR%28RAND%280%29%2A2%29%29%2F%2A%2A%2FHAVING%2F%2A%2A%2FMIN%280%29%23--%2F%2A%2A%2F-

In den SQL-Kommandos werden die Daten mit addslashes behandelt. Wenn ich

?TID=17540+%2F%2A%2A%2F%27%2F%2A%2A%2FOR%2F%2A%2A%2F%28SELECT%2F%2A%2A%2F5808%2F%2A%2A%2FFROM%28SELECT%2F%2A%2A%2FCOUNT%28%2A%29%2CCONCAT%280x64757375%2C%28SELECT%2F%2A%2A%2FMID%28%28IFNULL%28CAST%28CONCAT%28_email_tickets_ALT%2C0x3a%2Cuser_kennwort%29%2F%2A%2A%2FAS%2F%2A%2A%2FNCHAR%29%2C0x20%29%29%2C1%2C55%29%2F%2A%2A%2FFROM%2F%2A%2A%2F%60remso%60.%60bia_termine%60%2F%2A%2A%2FORDER%2F%2A%2A%2FBY%2F%2A%2A%2F%60id%60%2F%2A%2A%2FLIMIT%2F%2A%2A%2F856%2C1%29%2C0x4b304e38%2CFLOOR%28RAND%280%29%2A2%29%29x%2F%2A%2A%2FFROM%2F%2A%2A%2FINFORMATION_SCHEMA.PLUGINS%2F%2A%2A%2FGROUP%2F%2A%2A%2FBY%2F%2A%2A%2Fx%29a%29%23%2F%2A%2A%2FcWfa

selbst eingebe, kommt diese Fehlermeldung:

Meldung 08.12.2021 22:21:27  

1062: Duplicate entry 'aguNosmer@localhostrA0j1' for key 'group_key'

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# welche sprachen sind vorhanden?
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SELECT
 ueb1.sprache
FROM      bia_uebersetzungen ueb1
WHERE     ueb1.tabelle    = 'termine'
AND       ueb1.tabelle_id = '17540 /**/'/**/OR/**/1/**/GROUP/**/BY/**/CONCAT(0x6167754e,(SELECT/**/MID(IFNULL(USER(),/**/0x20),1,55)),0x7241306a,FLOOR(RAND(0)*2))/**/HAVING/**/MIN(0)#--/**/-'

und dann nachmittags nochmals 1616 derartiger Aufrufe.

Allein, um die abzuwehren ist der Server ja beschäftigt. Aber kann das Schaden an den Daten anrichten? Was wird da versucht?

Wenn ich mit phpmyadmin schaue, ist der Schlüsselname group_key in Tabelle bia_uebersetzungen nicht vorhanden.

Gruß, Linuchs

  1. Hallo,

    Heute morgen 5:28 und 5:29 kamen 211 Aufrufe in dieser Form:

    /?TID=17540+%2F%2A%2A%2F%27%2F%2A%2A%2FOR%2F%2A%2A%2F1%2F%2A%2A%2FGROUP%2F%2A%2A%2FBY%2F%2A%2A%2FCONCAT%280x6167754e%2C%28SELECT%2F%2A%2A%2FMID%28IFNULL%28USER%28%29%2C%2F%2A%2A%2F0x20%29%2C1%2C55%29%29%2C0x7241306a%2CFLOOR%28RAND%280%29%2A2%29%29%2F%2A%2A%2FHAVING%2F%2A%2A%2FMIN%280%29%23--%2F%2A%2A%2F-

    lass uns mal die URL-Codierung auflösen und einen Schritt in Richtung Klartext gehen:

    /?TID=17540+/**/'/**/OR/**/1/**/GROUP/**/BY/**/CONCAT(0x6167754e,(SELECT/**/MID(IFNULL(USER(),/**/0x20),1,55)),0x7241306a,FLOOR(RAND(0)*2))/**/HAVING/**/MIN(0)#--/**/-

    Ergibt das irgendeinen Sinn? Stufe 2: Lass uns die leeren Kommentare entfernen.

    /?TID=17540+'OR 1 GROUP BY CONCAT(0x6167754e,(SELECT MID(IFNULL(USER(), 0x20),1,55)),0x7241306a,FLOOR(RAND(0)*2)) HAVING MIN(0)#---

    Sieht für mich nach einem stümperhaften SQL-Angriff aus.

    In den SQL-Kommandos werden die Daten mit addslashes behandelt.

    Das ist Unsinn.

    Allein, um die abzuwehren ist der Server ja beschäftigt. Aber kann das Schaden an den Daten anrichten? Was wird da versucht?

    Solange dein Script oder dein DBMS das als Unsinn erkennt und abweist, ist ja alles okay. Das ist dann etwa so, als ob jemand mit einem x-beliebigen Schlüssel probiert, ob der nicht vielleicht zu deiner Wohnungstür passt.

    May the Schwartz be with you
     Martin

    --
    Wenn ich den See seh, brauch ich kein Meer mehr.
    1. Hallo Martin,

      nach der Fehlermeldung kommt dann die ganz normale Seite, d.h. der Parameter TID (termin_id) wird korrekt ausgewertet und eine Veranstaltungstermin gezeigt.

      Das dauert so um 0.2 sec und wenn 1000 Aufrufe kommen läppert sich's.

      Mache jetzt gleich nach Start des Programms dies:

      /* ==================
       * Angriffe abfangen
       * ================== */
      $query  = strtolower( str_replace( "%20", " ", $_SERVER['QUERY_STRING'] ));
      $fehler = "Fehler ";
      if ( strlen( $query ) > 100 )                 $fehler   .= $query . " > 100 Zeichen ";
      if ( strpos( $query, "delete " ) !== FALSE )  $fehler  .=  "001 ";
      if ( strpos( $query, "insert " ) !== FALSE )  $fehler  .=  "002 ";
      if ( strpos( $query, "select " ) !== FALSE )  $fehler  .=  "003 ";
      if ( strpos( $query, "update " ) !== FALSE )  $fehler  .=  "004 ";
      if ( strlen( $fehler ) > 7 ) {
        exit( $fehler );
      }
      
      1. Moin Linuchs,

        du doktorst an den Symptomen herum ohne das zu Grunde liegende Problem zu beheben. Verwende

        Viele Grüße
        Robert

    2. Hallo Martin,

      das ist schon ein "Bobby Tables"-Angriff - aber merkwürdig ist dieses Gruppieren des Ergebnisses nach USER(), der zwei scheinbar sinnlose Strings eingepackt ist und noch ein Zufallselement dazu bekommt.

      Die leeren Kommentare sind Ersatz-Whitespace und maskieren den SQL Text ein wenig.

      In Linuchs' Kontext doof, in anderen Kontexten möglicherweise verheerend. Vielleicht haben diese Hex-Angaben in anderen DBMS auch noch andere Wirkungen.

      Wie auch immer, Linuchs - addslashes ist die falsche Antwort auf diesen Angriff. Die richtige lautet mysqli_real_escape_string, wie schon drölfzigmal geschrieben. Machmal sollte man sich auch an Handbuchempfehlungen halten.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Tach!

        Wie auch immer, Linuchs - addslashes ist die falsche Antwort auf diesen Angriff. Die richtige lautet mysqli_real_escape_string, wie schon drölfzigmal geschrieben. Machmal sollte man sich auch an Handbuchempfehlungen halten.

        Der Punkt ist nicht, ob man addslashes() oder mysqli_real_escape_string() verwendet. Der Punkt ist, ob man dem Kontext entsprechend korrekt behandelt. Beide Funktionen arbeiten nur dann wirksam, wenn man im Stringkontext ist. Das was mysqli_real_escape_string() zusätzlich zu add_slashes() maskiert ist für SQL-Statements mit ASCII-basierter Zeichenkodierung nicht relevant.

        In Linuchs Posting ist ein Felermeldungtext zu sehen mit einem Statement, bei dem das zweite ' in der letzten Zeile nicht maskiert ist, und somit die SQL-Injection wirksam aussieht. Dazu kommt aber die Aussage, dass addslashes() angewendet sei. Nun, dem Anschein nach zumindest nicht für den Fehlermeldungstext. Ob das eigentliche Statement wirksam behandelt wurde, ist dem Posting nicht zu entnehmen.

        dedlfix.

        1. Hallo dedlfix,

          nein, das letzte ' in der FM dürfte aus seinem ursprünglichen SQL stammen.

          Sowas in dieser Art:

          $id = addslashes($_GET['ID']);
          $sql = "SELECT foo FROM bar WHERE id='$id'";
          //                                       ^ dieses hier
          

          Beide Funktionen arbeiten nur dann wirksam, wenn man im Stringkontext ist.

          Dass tabelle_id ein int ist und kein char/varchar, wäre anzunehmen. Aber sicher wissen wir das nicht. MYSQL hat sich ja das Type Juggling bei PHP abgeguckt und errät nach bestem Wissen und Gefühl die User-Absichten. Gelegentlicher Zehenverlust bei Schüssen in den Fuß nicht ausgeschlossen. Dann doch lieber ein ehrlicher Type Mismatch Error.

          Wenn's eine INT Column ist, hast Du recht. Wenn man einen Integerwert erwartet, prüft man nicht auf einen String. Dann nimmt man intval() und schaltet eventuell eine /\d+/ Regex vor.

          Empfohlen wird manchmal auch die filter_input-Funktion mit FILTER_VALIDATE_INT. Ich selbst mag die filter-Funktionen nicht so, weil die die Seuche mit führenden Nullen und Oktalzahlen haben.

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Tach!

            nein, das letzte ' in der FM dürfte aus seinem ursprünglichen SQL stammen.

            Worauf bezieht sich das "nein"? Ich meinte schon das zweite und nicht das letzte. Das was zwischen den beiden ersten Kommentaren gleich hinter der Zahl vom Anfang steht.

            Dass tabelle_id ein int ist und kein char/varchar, wäre anzunehmen. Aber sicher wissen wir das nicht. MYSQL hat sich ja das Type Juggling bei PHP abgeguckt und errät nach bestem Wissen und Gefühl die User-Absichten. Gelegentlicher Zehenverlust bei Schüssen in den Fuß nicht ausgeschlossen. Dann doch lieber ein ehrlicher Type Mismatch Error.

            Ich seh das nicht als tragisch an. Ob in dem SQL-Statement zuerst ein String erkannt wird und anschließend die Zahl daraus konvertiert wird, oder ob die Zahl direkt aus einem Zahlenliteral konvertiert wird, macht das Kraut nicht fett. In beiden Fälle steht sie da als String.

            Wenn's eine INT Column ist, hast Du recht. Wenn man einen Integerwert erwartet, prüft man nicht auf einen String. Dann nimmt man intval() und schaltet eventuell eine /\d+/ Regex vor.

            Empfohlen wird manchmal auch die filter_input-Funktion mit FILTER_VALIDATE_INT. Ich selbst mag die filter-Funktionen nicht so, weil die die Seuche mit führenden Nullen und Oktalzahlen haben.

            intval() erkennt auch Oktalzahlen.

            dedlfix.

            1. Ich seh das nicht als tragisch an. Ob in dem SQL-Statement zuerst ein String erkannt wird und anschließend die Zahl daraus konvertiert wird, oder ob die Zahl direkt aus einem Zahlenliteral konvertiert wird, macht das Kraut nicht fett. In beiden Fälle steht sie da als String.

              Aus der hiesigen Argumentation finde ich es auch nicht weiter schlimm. Aber: damit kann man sich bei Mysl/MariaDB böse Performanceeinbußen einfangen, wenn man in einer Query z.B. auf '1' abfragt, obwohl es sich um ein indiziertes Zahlenfeld handelt. Wie immer, merkt man natürlich erst bei entsprechend großen Datenmengen. „Klappen“ tuts auch mit dem Cast.

              1. Tach!

                Ich seh das nicht als tragisch an. Ob in dem SQL-Statement zuerst ein String erkannt wird und anschließend die Zahl daraus konvertiert wird, oder ob die Zahl direkt aus einem Zahlenliteral konvertiert wird, macht das Kraut nicht fett. In beiden Fälle steht sie da als String.

                Aus der hiesigen Argumentation finde ich es auch nicht weiter schlimm. Aber: damit kann man sich bei Mysl/MariaDB böse Performanceeinbußen einfangen, wenn man in einer Query z.B. auf '1' abfragt, obwohl es sich um ein indiziertes Zahlenfeld handelt. Wie immer, merkt man natürlich erst bei entsprechend großen Datenmengen. „Klappen“ tuts auch mit dem Cast.

                Ich stelle da keinen Unterschied bei einem EXPLAIN fest. In beiden Fällen wird der Index als nutzbar angegeben. Die Laufzeiten sind auch nicht signifikant unterschiedlich. Mein Test hatte ca. 3 Mio Datensätze.

                dedlfix.

                1. Ich stelle da keinen Unterschied bei einem EXPLAIN fest. In beiden Fällen wird der Index als nutzbar angegeben. Die Laufzeiten sind auch nicht signifikant unterschiedlich. Mein Test hatte ca. 3 Mio Datensätze.

                  Hmmm… Also ich hatte den Fall schon ein paar Male. Das EXPLAIN hatte da auch keinen Unterschied gemeldet, der Laufzeitunterschied war dennoch signifikant.

                  Dann muss wohl an der im Einsatz befindlichen Version des DB-Servers und/oder der Query gelegen haben. Die waren jeweils recht umfangreich mit vielen Bedingungen…

  2. Moin Linuchs,

    Heute morgen 5:28 und 5:29 kamen 211 Aufrufe in dieser Form:

    /?TID=17540+%2F%2A%2A%2F%27%2F%2A%2A%2FOR%2F%2A%2A%2F1%2F%2A%2A%2FGROUP%2F%2A%2A%2FBY%2F%2A%2A%2FCONCAT%280x6167754e%2C%28SELECT%2F%2A%2A%2FMID%28IFNULL%28USER%28%29%2C%2F%2A%2A%2F0x20%29%2C1%2C55%29%29%2C0x7241306a%2CFLOOR%28RAND%280%29%2A2%29%29%2F%2A%2A%2FHAVING%2F%2A%2A%2FMIN%280%29%23--%2F%2A%2A%2F-

    welcher Datentyp wird denn für den Wert von TID erwartet?

    In den SQL-Kommandos werden die Daten mit addslashes behandelt.

    Das war, ist und wird niemals keine gute Idee (sein). Verwende

    Meldung 08.12.2021 22:21:27

    1062: Duplicate entry 'aguNosmer@localhostrA0j1' for key 'group_key'

    Bei so einer Fehlermeldung für „Garbage In“ würde ich aber sehr stutzig werden, denn es heißt nicht, dass eine offensichtlich falsche ID übergeben, sondern anderer SQL-Code ausgeführt worden ist. Das ist potenziell gefährlich!

    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # welche sprachen sind vorhanden?
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    SELECT
     ueb1.sprache
    FROM      bia_uebersetzungen ueb1
    WHERE     ueb1.tabelle    = 'termine'
    AND       ueb1.tabelle_id = '17540 /**/'/**/OR/**/1/**/GROUP/**/BY/**/CONCAT(0x6167754e,(SELECT/**/MID(IFNULL(USER(),/**/0x20),1,55)),0x7241306a,FLOOR(RAND(0)*2))/**/HAVING/**/MIN(0)#--/**/-'
    

    Ich vereinfache mal:

    SELECT ueb1.sprache
        FROM  bia_uebersetzungen ueb1
        WHERE ueb1.tabelle = 'termine'
        AND ueb1.tabelle_id = '17540 /**/'OR 1
        GROUP BY CONCAT(0x6167754e,(
            SELECT MID(IFNULL(USER(),0x20),1,55)),0x7241306a,FLOOR(RAND(0)*2))
        HAVING MIN(0)
    

    Ich wette, dass deine Intention vor dem OR in Zeile 4 endet.

    Allein, um die abzuwehren ist der Server ja beschäftigt.

    Im Grunde genommen kann der Server schon das Handtuch (zurück-) werfen, wenn da keine gültige „TID“ ankommt – dafür muss kein einziges SQL-Statement ausgeführt werden.

    Aber kann das Schaden an den Daten anrichten?

    Anscheinend kann da beliebiger SQL-Code eingeschleust werden, also lautet die Antwort Ja.

    Viele Grüße
    Robert

    1. Tach!

      In den SQL-Kommandos werden die Daten mit addslashes behandelt.

      Das war, ist und wird niemals keine gute Idee (sein). Verwende

      Für die Sicherheit von SQL-Statements bleibt es sich gleich, wenn man mit ASCII-basierter Zeichenkodierung arbeitet.

      Die zusätzliche Funktionalität von mysqli_real_escape_string() ist die Beachtung von Zeichenkodierungen, was nur bei einigen "exotischen" Kodierungen wichtig ist. Und es behandelt zusätzlich die Zeichen \n, \r und Ctrl-Z, aber die spielen für SQL-Statements keine Rolle.

      Zitat MySQL-Handbuch

      • Characters encoded are , ', ", NUL (ASCII 0), \n, \r, and Control+Z. Strictly speaking, MySQL requires only that backslash and the quote character used to quote the string in the query be escaped. mysql_real_escape_string() quotes the other characters to make them easier to read in log files.

      Sie sind nur für Logfiles interessant, und das nicht einmal aus Sicherheitsaspekten. Wenn man diese nicht auswertet oder gar nicht eingeschaltet hat, ist es auch egal, ob da zuviele Zeilenumbrüche drin stehen.

      Es bleibt sich also gleich, ob man die notwendigen Backslash- und Quote-Zeichen mit mysqli_real_escape_string(), mit addslashes() oder einer beliebigen anderen Funktion maskieren lässt. Wichtig ist nur, dass es korrekt angewendet wird. Das ledigliche (Nicht-)Auftauchen von mysqli_real_escape_string() im Code ist kein Zeichen für fehlende oder enthaltene Sicherheit.

      Damit umgeht man die Notwendigkeit des Maskierens, aber auch das muss korrekt verwendet werden. Sprich: alle variablen Werte müssen durch den Prepared-Statements-Mechanismus gehen (oder anderweitig beachtet werden).

      dedlfix.

      1. Hi,

        Es bleibt sich also gleich, ob man die notwendigen Backslash- und Quote-Zeichen mit mysqli_real_escape_string(), mit addslashes() oder einer beliebigen anderen Funktion maskieren lässt.

        Bis in einer zukünftigen MySQL-Version weitere Zeichen dazukommen, die von addSlashes nicht erfaßt werden.

        Verwendet man gleich die dafür vorgesehene Funktion mysqli_real_esacpe_string statt einer, die derzeit zufällig fast das gleiche macht, ist man auf der sicheren Seite.

        cu,
        Andreas a/k/a MudGuard

        1. Tach!

          Es bleibt sich also gleich, ob man die notwendigen Backslash- und Quote-Zeichen mit mysqli_real_escape_string(), mit addslashes() oder einer beliebigen anderen Funktion maskieren lässt.

          Bis in einer zukünftigen MySQL-Version weitere Zeichen dazukommen, die von addSlashes nicht erfaßt werden.

          Das halte ich für äußerst unwahrscheinlich, dass ein weiteres Zeichen zum Quotieren von Strings hinzukommen wird, welches man dann maskieren müsste. Zudem ändern weitere Zeichen am grundsätzlichen Problem nichts.

          Verwendet man gleich die dafür vorgesehene Funktion mysqli_real_esacpe_string statt einer, die derzeit zufällig fast das gleiche macht, ist man auf der sicheren Seite.

          Nein, nicht die Funktion ist entscheidend, sondern ihre korrekte Anwendung. Das sollte man nun langsam erkannt haben. addslashes() behandelt alle für das Quotieren notwendigen Zeichen. Wenn sie falsch angewendet wurde - was hier der Fall zu sein scheint - nützt es nichts, sie gegen eine anderen Funktion auszutauschen, die auf gleiche Weise falsch angewendet wird.

          Es wäre nun schön, wenn wir nicht nur Indizien sehen (fertiges Statement mit SQL-Injection plus Fehlermeldung, bei der ein in der Injection steckendes GROUP BY ein Problem verursacht), sondern auch den Code, der das Statement zusammenbaut.

          dedlfix.

        2. Tach!

          Es bleibt sich also gleich, ob man die notwendigen Backslash- und Quote-Zeichen mit mysqli_real_escape_string(), mit addslashes() oder einer beliebigen anderen Funktion maskieren lässt.

          Bis in einer zukünftigen MySQL-Version weitere Zeichen dazukommen, die von addSlashes nicht erfaßt werden.

          Wer gibt denn eine Garantie, dass mysql(i)_real_escape_string()[1] die Funktion ist, die in der Zukunft den (unwahrscheinlichen) Fall abdeckt, hinzukommende für SQL-Injection relevante Zeichen oder andere Umstände zu berücksichtigen?

          Verwendet man gleich die dafür vorgesehene Funktion mysqli_real_esacpe_string statt einer, die derzeit zufällig fast das gleiche macht, ist man auf der sicheren Seite.

          mysql_escape_string() wurde mit PHP 4.1 eingeführt. Davor gab es nur addslashes() (Magic Quotes ignorieren wir mal). Damals hieß es schon, dass man von addslashes() auf die mysql-eigene Funktion umsteigen solle. Aber war das zukunftssicher? Jedenfalls kam mit PHP 4.3 dann mysql_real_escape_string() dazu, weil die Funktion zwar die notwendigen Zeichen (plus Zusatzzeichen \n, \r, ^Z) behandelt hatte, aber nicht berücksichtigt hatte, dass es Zeichenkodierungen abseits von ASCII-basierenden gibt, bei denen die Funktion versagte. Man hat sie nicht gändert, sondern die neue Funktion mysql_real_escape_string() dazugegeben. Die alte Funktion wurde nicht erweitert, weil sie wegen eines neuen Pflichtparameters nicht kompatibel war.

          Aus der Geschichte kann man schlecht auf die Zukunft schließen ... aber ... es gibt bereits einen Nachfolger in der MySQL-Client-API, mit dem Namen mysql_real_escape_string_quote(). Man hat eine Situation gefunden hat, in der mysql_real_escape_string() nicht ausreicht. Die neue Funktion benötigt einen zusätzlichen Parameter, weswegen die alte Funktion nicht abwärtskompatibel weiterverwendet werden kann. In PHP ist die Funktion noch nicht angekommen. Mit der Aussage, mysql(i)_real_escape_string() sei zukunftssicher, würde ich mich jedenfalls schwertun.

          dedlfix.


          1. In PHP mit i, in der MySQl-Client-API ohne i. ↩︎

          1. Hallo dedlfix,

            Man hat eine Situation gefunden hat, in der mysql_real_escape_string() nicht ausreicht.

            Ich finde die Beispiele in der MYSQL Doku nicht sehr hilfreich und ich habe auch jetzt keinen Toolstack hier, um zum Probieren ein C Programm zu schreiben (da wäre auch erstmal ein Schutthaufen über dem C-Wissen wegzuschippen...)

            Aber der Prosatext sagt, dass diese Funktion dazu da ist, Logfiles besser lesbar zu machen. Und der einzige Weg, auf dem sie das tun könnte, scheint mir darin zu bestehen, dass nur das genannte Quote escaped wird und die übrigen nicht. D.h. diese Funktion schließt keine Sicherheitslücke, sondern entfernt unnötiges Quoting zu Gunsten der Lesbarkeit.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo Rolf,

              Ich finde die Beispiele in der MYSQL Doku nicht sehr hilfreich und ich habe auch jetzt keinen Toolstack hier, um zum Probieren ein C Programm zu schreiben (da wäre auch erstmal ein Schutthaufen über dem C-Wissen wegzuschippen...)

              so richtig klar finde ich die Erklärungen auch nicht, wenn ich auch immerhin fließend C spreche.

              Aber der Prosatext sagt, dass diese Funktion dazu da ist, Logfiles besser lesbar zu machen.

              Das sagt er bei beiden Funktionen. Deshalb escapen beide Funktionen nicht nur den Backslash und die Anführungszeichen (was ausreichen würde, um das SQL-Statement "sicher" zu machen), sondern auch einige ASCII-Steuerzeichen, die eine Protokolldatei entstellen würden (CR, LF, EOF, NUL).

              Der entscheidende Unterschied ist, so wie ich die Beschreibungen verstehe, dass man bei mysql_real_escape_string_quote() noch angeben kann, welchen String-Delimiter man denn im SQL-Statement verwendet. Ist das beispielsweise das Double Quote 0x22, dann können Single Quotes 0x27 oder Backticks 0x60 unmaskiert bleiben und die Logdatei wird tatsächlich besser lesbar, wenn diese Zeichen noch in den Daten vorkommen. Das ist aber IMHO wirklich ein Luxusfeature.

              Und der einzige Weg, auf dem sie das tun könnte, scheint mir darin zu bestehen, dass nur das genannte Quote escaped wird und die übrigen nicht. D.h. diese Funktion schließt keine Sicherheitslücke, sondern entfernt unnötiges Quoting zu Gunsten der Lesbarkeit.

              Genua.[tm]

              Immer eine Handbreit Wasser unterm Kiel
               Martin

              --
              Wenn ich den See seh, brauch ich kein Meer mehr.
            2. Tach!

              Aber der Prosatext sagt, dass diese Funktion dazu da ist, Logfiles besser lesbar zu machen. Und der einzige Weg, auf dem sie das tun könnte, scheint mir darin zu bestehen, dass nur das genannte Quote escaped wird und die übrigen nicht. D.h. diese Funktion schließt keine Sicherheitslücke, sondern entfernt unnötiges Quoting zu Gunsten der Lesbarkeit.

              Ich hab es nicht genau gelesen, aber wenn das so ist, entspricht das ungefähr dem Unterschied zwischen addslashes() und mysql_real_escape_string(). Das was mysql_real_escape_string() mehr macht, ist für die Sicherheit von SQL-Statements nicht relevant. Der entscheidende Unterschied ist nur die Beachtung der auf der Verbindung eingestellten Zeichenkodierung, was aber keinen Unterschied bei ASCII-basierten Kodierungen ergibt.

              dedlfix.

  3. Hallo Linuchs,

    ich habe noch etwas recherchiert, denn ein DUPLICATE KEY in einem SELECT Statement ist eigentlich der totale Unsinn, duplicate keys sind etwas, das bei UPDATE oder INSERT passieren kann.

    'group_key' ist ein technischer Schlüssel, den der DB Server intern bei der Ausführung von GROUP BY verwendet.

    Und siehe da: https://bugs.mysql.com/bug.php?id=58081

    Deine Maria ist relativ alt und könnte diesen Bug ebenfalls haben (der wurde in MYSQL 5.7.? gefixt).

    So - und nun hast Du ein Problem, denn der GROUP BY kommt aus der SQL Injection und damit ist der Angriff offenbar durch deine addslashes Maskierung durchgekommen. Entweder hast Du addslashes an dieser Stelle vergessen, oder irgendwie seinen Input statt des Outputs verwendet.

    Dringender Appell: Ermittle den korrekten Datentyp für diese ID. Ist sie integer, prüfe die übergebene ID ebenfalls auf "nur Ziffern". Steht da was anderes, führe das SQL nicht aus. Eine weitergehende Maskierung ist bei bestandener Prüfung nicht mehr nötig.

    Rolf

    --
    sumpsi - posui - obstruxi
  4. Scheinbar ist schon alles gesagt.

    Aber, damit das nicht untergeht hier noch mal die wichtige Nachricht von Rolf B:

    So - und nun hast Du ein Problem, denn der GROUP BY kommt aus der SQL Injection und damit ist der Angriff offenbar durch deine addslashes Maskierung durchgekommen. Entweder hast Du addslashes an dieser Stelle vergessen, oder irgendwie seinen Input statt des Outputs verwendet.

    Ansonsten hätte ich noch folgenden Kommentar:

    Sieht aus wie ein Angriff auf eine hackbare Version von MyBB. Auch sowas ist absolut Alltag, das erlebt jeder ein paar Mal und es ist nicht gegen Dich gerichtet.

    Wenn Du willst, dass die Angreifer, die Dir Unsinn schicken, damit aufhören, dann solltest Du sicher stellen, dass die eine Fehlermeldung mit einem Fehlercode (un sei es der 404er) bekommen.

    Um den lästigen Angreifer loszuwerden würde ich zusätzlich vor dem Abarbeiten von auch nur irgendwas prüfen, ob $_GET['TID'] etwas anderes als Ziffern enthält und den Request (da auch ich ja auch ein Spaßvogel bin) mit Errorcode 418 („I'm a teapot“) ohne weiteren Inhalt abweisen.

    Auch fail2ban kann helfen und ggf. IP-Adressen, die zu oft im error-log auftauchen der Firewall zu Sperrung vorschlagen.

    1. Hallo Raketenschnelldiagnostiker,

      $_GET['TID'] etwas anderes als Ziffern enthält

      ich bin ja bekanntlich kein Indianer aus dem Süden, eher aus dem Nordosten, und mich würde interessieren, ob man sowas sogar gleich im Apache abfackeln kann, damit der gar nicht den PHP Interpreter damit behelligen muss.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Hallo,

        $_GET['TID'] etwas anderes als Ziffern enthält

        ich bin ja bekanntlich kein Indianer aus dem Süden, eher aus dem Nordosten

        ich musste tatsächlich einen Moment über diesen Satz nachdenken. 😉

        und mich würde interessieren, ob man sowas sogar gleich im Apache abfackeln kann, damit der gar nicht den PHP Interpreter damit behelligen muss.

        Im Rahmen von URL Rewriting kann man in einer RewriteCond auf Teile der Request URL eingehen. Aber der Query-String ist da meines Wissens außen vor.

        Andere Möglichkeiten, den Indianer schon auf URL-Parameter anzusetzen, fallen mir im Moment auch nicht ein. Das muss aber nichts heißen.

        Kann das denn der Konkurr Außenseiter[1] aus Redmond?

        May the Schwartz be with you
         Martin

        --
        Wenn ich den See seh, brauch ich kein Meer mehr.

        1. Zumindest im kommerziellen Webhosting-Bereich ist er das wohl. ↩︎

        1. Hallo Der,

          eher aus dem Nordosten...

          ich musste tatsächlich einen Moment über diesen Satz nachdenken.

          Zu Recht. Denn er war idiotisch.

          Microsoft ist schließlich im Nordwesten. Im Nordosten findet man IBM. Und die Apache Foundation, in Wakefield, MA.

          Rolf, Dussel!

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Hallo Rolf,

            eher aus dem Nordosten...

            ich musste tatsächlich einen Moment über diesen Satz nachdenken.

            Zu Recht. Denn er war idiotisch.

            naja, nicht ganz.

            Microsoft ist schließlich im Nordwesten.

            Klar. Ich hatte bei Nordosten auch erst das MIT im Verdacht, konnte aber keinen konkreten Bezug herstellen. Dann fiel mir Microsoft und Redmond ein, und ich hab's einfach auf die sehr verbreitete Ost-West-Schwäche geschoben.

            Die Apachen sind aber tatsächlich eher im Süden der USA zu verorten.

            Im Nordosten findet man IBM. Und die Apache Foundation, in Wakefield, MA.

            Das wusste ich wiederum nicht, danke.

            May the Schwartz be with you
             Martin

            --
            Wenn ich den See seh, brauch ich kein Meer mehr.
        2. Tach!

          Im Rahmen von URL Rewriting kann man in einer RewriteCond auf Teile der Request URL eingehen. Aber der Query-String ist da meines Wissens außen vor.

          RewriteCond ist der Teil des Rewrite-Mechanismus, der Teile jenseits des Pfades berücksichtigen kann, darunter auch den %{QUERY_STRING}. Die RewriteRule kann hingegen nur den Pfad auswerten.

          Es gibt auch noch RewriteMap, aber das muss mindestens im VHost konfiguriert werden, ist also oft nicht verwendbar. Damit kann man auch selbst geschriebene Programme laufen lassen und individuelle Umschreibungen vornehmen. Die Dokumentation schweigt sich aber darüber aus, ob nur der Pfad oder die komplette URL übergeben wird.

          Generell würde ich das aber mit Vorsicht genießen, denn wenn man die Prüfung zum Apachen hin verlagert, man hat die Programmlogik an zwei Stellen zu beachten.

          dedlfix.

          1. Hallo dedlfix,

            man hat die Programmlogik an zwei Stellen zu beachten.

            Da ist was dran. Ich dachte nur an Linuchs' Sorge, dass sein Server in die Knie geht. Wenn man sich den PHP Aufruf sparen kann, hat man ggf. was gewonnen. Keine Ahnung, wie effizient das Code-Caching bei FastCGI oder einem mod_php funktioniert.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Hallo dedlfix,

              man hat die Programmlogik an zwei Stellen zu beachten.

              Da ist was dran. Ich dachte nur an Linuchs' Sorge, dass sein Server in die Knie geht. Wenn man sich den PHP Aufruf sparen kann, hat man ggf. was gewonnen. Keine Ahnung, wie effizient das Code-Caching bei FastCGI oder einem mod_php funktioniert.

              Rolf

              Also der Ergebniscache kann nicht genutzt werden, es sei denn der Angreifer schickt mehrfach identische Daten.

              Aber der (Bin/Bytecode)-cache wird wunderbar arbeiten. Auf einem Server dürfte

              <?php
              if (
                 isset( $_GET['TID'] ) 
                 AND ! is_numeric( $_GET['TID'] ) 
              ) {
              
                 http_response_code(418);
                 echo "I'am a teapot!";
                 exit;
              }
              

              Nur den Bruchteil einer tausendstel Sekunde benötigen.

              1. Hab es mal gemessen.

                0.0021457672119141 Millisekunden waren es gerade im Negativfall. 0.014066696166992 Millisekunden waren es gerade im Positivfall. (vermutlicher Angriff)

                <?php
                $_GET['TID']="11.1ia";
                
                $start=microtime(1);
                if (
                   isset( $_GET['TID'] )
                   AND ! is_numeric( $_GET['TID'] )
                ) {
                
                   http_response_code(418);
                   echo "I'am a teapot!";
                   $ende=microtime(1);
                   echo ($ende - $start)*1000 . " Millisekunden";
                   exit;
                }
                $ende=microtime(1);
                echo ($ende - $start)*1000 . " Millisekunden";