
JavaScript에서 URL, 경로, 비밀값 및 기타 흥미로운 정보를 추출합니다.
jsluice는 JavaScript 소스 코드에서 URL, 경로, 비밀키(secret) 및 기타 흥미로운 데이터를 추출하기 위한 Go 패키지이자 명령줄 도구입니다.
바로 사용해보고 싶다면: 명령줄 도구를 살펴보세요.
jsluice의 기능을 자신의 프로젝트에 통합하려면: examples를 살펴보고 패키지 문서를 읽어보세요.
명령줄 도구를 설치하려면 다음을 실행하세요:
▶ go install github.com/BishopFox/jsluice/cmd/jsluice@latest
패키지를 프로젝트에 추가하려면 다음을 실행하세요:
▶ go get github.com/BishopFox/jsluice
jsluice는 정규식만 사용하는 대신 go-tree-sitter를 사용하여 URL이 사용되는 것으로 알려진 위치(예: document.location에 할당되거나, window.open()에 전달되거나, fetch()에 전달되는 등)를 찾습니다.
간단한 예제 프로그램은 여기에 있습니다:
analyzer := jsluice.NewAnalyzer([]byte(`
const login = (redirect) => {
document.location = "/login?redirect=" + redirect + "&method=oauth"
}
`))
for _, url := range analyzer.GetURLs() {
j, err := json.MarshalIndent(url, "", " ")
if err != nil {
continue
}
fmt.Printf("%s\n", j)
}
예제 실행:
▶ go run examples/basic/main.go
{
"url": "/login?redirect=EXPR\u0026method=oauth",
"queryParams": [
"method",
"redirect"
],
"bodyParams": [],
"method": "GET",
"type": "locationAssignment",
"source": "document.location = \"/login?redirect=\" + redirect + \"\u0026method=oauth\""
}
redirect 쿼리 문자열 매개변수의 값이 EXPR인 것에 유의하세요.
이러한 코드는 JavaScript에서 흔히 볼 수 있습니다:
document.location = "/login?redirect=" + redirect + "&method=oauth"
jsluice는 문자열 연결(string concatenation)을 이해하며, 값을 알 수 없는 표현식은 EXPR로 대체합니다.
완벽한 해결책은 아니지만, 이 접근 방식은 대부분의 경우 유효한 URL이나 경로를 만들어내며,
다른 접근 방식으로는 쉽게 발견할 수 없는 것들을 발견할 수 있게 해줍니다.
이 경우, 단순한 정규식은 method 쿼리 문자열 매개변수를 놓칠 가능성이 높습니다:
▶ JS='document.location = "/login?redirect=" + redirect + "&method=oauth"'
▶ echo $JS | grep -oE 'document\.location = "[^"]+"'
document.location = "/login?redirect="
jsluice에는 일반적인 시나리오를 위한 내장 URL 매처가 포함되어 있지만, AddURLMatcher 함수로 더 추가할 수 있습니다:
analyzer := jsluice.NewAnalyzer([]byte(`
var fn = () => {
var meta = {
contact: "mailto:[email protected]",
home: "https://example.com"
}
return meta
}
`))
analyzer.AddURLMatcher(
// jsluice.URLMatcher 구조체의 첫 번째 값은 찾을 노드 유형입니다.
// "string", "assignment_expression", "call_expression" 중 하나일 수 있습니다.
jsluice.URLMatcher{"string", func(n *jsluice.Node) *jsluice.URL {
val := n.DecodedString()
if !strings.HasPrefix(val, "mailto:") {
return nil
}
return &jsluice.URL{
URL: val,
Type: "mailto",
}
}},
)
for _, match := range analyzer.GetURLs() {
fmt.Println(match.URL)
}
이 예제의 사본은 여기에 있습니다. 다음과 같이 실행할 수 있습니다:
▶ go run examples/urlmatcher/main.go
mailto:[email protected]
https://example.com
jsluice는 기본적으로 mailto: URI를 매칭하지 않으며, 위 결과는 사용자 정의 URLMatcher에 의해 발견된 것입니다.
jsluice는 URL뿐만 아니라 비밀키도 추출할 수 있습니다. URL 추출과 마찬가지로 기본 매처를 보완하기 위해 사용자 정의 매처를 제공할 수 있습니다. 이를 수행하는 짧은 예제 프로그램이 여기에 있습니다:
analyzer := jsluice.NewAnalyzer([]byte(`
var config = {
apiKey: "AUTH_1a2b3c4d5e6f",
apiURL: "https://api.example.com/v2/"
}
`))
analyzer.AddSecretMatcher(
// jsluice.SecretMatcher 구조체의 첫 번째 값은
// JavaScript 소스에서 실행할 tree-sitter 쿼리입니다.
jsluice.SecretMatcher{"(pair) @match", func(n *jsluice.Node) *jsluice.Secret {
key := n.ChildByFieldName("key").DecodedString()
value := n.ChildByFieldName("value").DecodedString()
if !strings.Contains(key, "api") {
return nil
}
if !strings.HasPrefix(value, "AUTH_") {
return nil
}
return &jsluice.Secret{
Kind: "fakeApi",
Data: map[string]string{
"key": key,
"value": value,
},
Severity: jsluice.SeverityLow,
Context: n.Parent().AsMap(),
}
}},
)
for _, match := range analyzer.GetSecrets() {
j, err := json.MarshalIndent(match, "", " ")
if err != nil {
continue
}
fmt.Printf("%s\n", j)
}
예제 실행:
▶ go run examples/secrets/main.go
[2023-06-14T13:04:16+0100]
{
"kind": "fakeApi",
"data": {
"key": "apiKey",
"value": "AUTH_1a2b3c4d5e6f"
},
"severity": "low",
"context": {
"apiKey": "AUTH_1a2b3c4d5e6f",
"apiURL": "https://api.example.com/v2/"
}
}
전체 JavaScript 소스에 대한 구문 트리(syntax tree)를 사용할 수 있기 때문에,
key와 value를 모두 검사할 수 있었고, 매칭된 부분에 대한 컨텍스트로 상위 객체를 쉽게 제공할 수도 있었습니다.