Bảo vệ lệnh shell với biến chuỗi


9

Trong một ngôn ngữ lập trình, tôi thực thi một lệnh shell đơn giản

cd var; echo > create_a_file_here

với var là một biến chứa một chuỗi (hy vọng) một thư mục đến nơi tôi muốn tạo tệp "create_a_file_here". Bây giờ nếu ai đó nhìn thấy dòng mã này, có thể khai thác nó bằng cách gán ví dụ:

var = "; rm -rf /"

Mọi thứ có thể trở nên khá xấu xí. Một cách để tránh trường hợp trên có thể là tìm kiếm chuỗi trong var cho một số ký tự đặc biệt như ';' trước khi thực hiện lệnh shell, nhưng tôi nghi ngờ điều này bao gồm tất cả các khai thác có thể.

Có ai biết một cách tốt để đảm bảo rằng "cd var" chỉ thay đổi một thư mục và không có gì khác không?


4
Tùy thuộc vào cách bạn có thể gọi shell, bạn cũng có thể chuyển varnhư một đối số. Ví dụ như gọi shvới đối số -c, 'cd "$1"; echo > create_a_file_here', 'sh', varcông trình và không cần bất kỳ thay đổi nào var. Đối 'sh'số được thông qua là $0.
ipsec

1
Ngôn ngữ lập trình gì? Bạn đang sử dụng POSIX sh, hoặc tạo ngôn ngữ lập trình của riêng bạn với cú pháp tương tự nhưng mở rộng varthay vì yêu cầu bạn viết cd "$var"? Hay đây là bashvới shopt -s cdable_vars? Ồ, tôi nghĩ rằng bạn có nghĩa là một số chương trình khác tạo ra một shell để chạy các lệnh này. Vì vậy, chỉ cần trích dẫn var, nhưng đảm bảo rằng nó không bao gồm chính một nhân vật trích dẫn ...
Peter Cordes

@PeterCordes Nếu bạn đang nói về Bash, một biến được trích dẫn kép có chứa một trích dẫn kép là tốt. ví dụ như s='"'; echo "$s"bản in ".
wjandrea

@WJAndrea: có, nhưng không có trích dẫn "át chủ bài" không thể bị đánh bại khi xây dựng một phép gán biến từ đầu vào không tin cậy. Oh, giải pháp: thực hiện var=untrusted stringtrong chương trình cha, vì vậy varmột biến môi trường đã được đặt khi gọi sh. Sau đó, bạn chỉ cần trích dẫn nó mỗi khi bạn mở rộng nó, đó là điều có thể làm đáng tin cậy. À, tôi thấy ý tưởng đó đã là một phần trong câu trả lời của Stéphane>. <
Peter Cordes

Câu trả lời:


9

Nếu tôi hiểu chính xác, varlà một biến trong ngôn ngữ lập trình của bạn.

Và trong ngôn ngữ lập trình của bạn, bạn đang yêu cầu một vỏ để diễn giải một chuỗi là nối của "cd ", nội dung của biến đó và "; echo > create_a_file_here".

Nếu sau đó, có, nếu nội dung của varkhông được kiểm soát chặt chẽ, đó là một lỗ hổng tiêm lệnh.

Bạn có thể thử và trích dẫn chính xác nội dung của biến¹ theo cú pháp của trình bao để nó được đảm bảo được truyền dưới dạng một đối số duy nhất cho cdnội dung.

Một cách tiếp cận khác là chuyển nội dung của biến đó theo cách khác. Một cách rõ ràng sẽ là vượt qua điều đó trong một biến môi trường. Chẳng hạn, trong C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Lần này, mã mà bạn yêu cầu shell giải thích đã được sửa, chúng ta vẫn cần viết đúng theo cú pháp của shell (ở đây được giả sử là shell tương thích POSIX):

  • mở rộng biến shell phải được trích dẫn để ngăn chia + toàn cầu
  • bạn cần -Pcho cdlàm một cách đơn giảnchdir()
  • bạn cần --đánh dấu sự kết thúc của các tùy chọn để tránh các vấn đề khi varbắt đầu với -(hoặc +trong một số shell)
  • Chúng tôi đặt thành CDPATHchuỗi trống trong trường hợp nó ở trong môi trường
  • Chúng tôi chỉ chạy echolệnh nếu cdthành công.

Có (ít nhất) một vấn đề còn lại: nếu var-, nó không chdir vào thư mục được gọi -nhưng đến thư mục trước đó (như được lưu trữ trong $OLDPWD) và OLDPWD=- CDPATH= cd -P -- "$DIR"không được bảo đảm để hoạt động xung quanh nó. Vì vậy, bạn cần một cái gì đó như:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Lưu ý rằng chỉ cần làm một system(concat("cd \"", var, "\"; echo..."));không con đường để đi, bạn muốn được chỉ cần di chuyển các vấn đề.

Ví dụ, một var = "$(rm -rf /)"vẫn sẽ là một vấn đề.

Cách đáng tin cậy duy nhất để trích dẫn văn bản cho các shell giống như Bourne là sử dụng các trích dẫn đơn cũng quan tâm đến các trích dẫn đơn có thể xảy ra trong chuỗi. Ví dụ, biến một char *var = "ab'cd"đểchar *escaped_var = "'ab'\\''cd'" . Đó là, thay thế tất cả 'để '\''và bọc toàn bộ bên trong '...'.

Đó vẫn giả định rằng chuỗi trích dẫn không được sử dụng trong backticks, và bạn vẫn muốn cần --, -P, &&, CDPATH=...


12

Giải pháp đơn giản: Đừng gọi shell từ chương trình của bạn. Ở tất cả.

Ví dụ của bạn ở đây là tầm thường, việc thay đổi thư mục và tạo tệp nên dễ dàng trong bất kỳ ngôn ngữ lập trình nào. Nhưng ngay cả khi bạn không cần phải chạy một lệnh bên ngoài, thường thì không cần phải thực hiện nó qua trình bao.

Vì vậy, ví dụ trong Python, thay vì chạy os.system("somecmd " + somearg), hãy sử dụng subprocess.run(["somecmd", somearg]). Trong C, thay vì system(), sử dụng fork()exec()(hoặc tìm một thư viện thực hiện nó).

Nếu bạn cần sử dụng shell, trích dẫn các đối số dòng lệnh hoặc chuyển chúng qua môi trường, như trong câu trả lời của Stéphane . Ngoài ra, nếu bạn thấy mình lo lắng về nhân vật đặc biệt, giải pháp chính xác là không cố gắng lọc ra (danh sách đen) các nhân vật nguy hiểm tiềm tàng, mà chỉ giữ các ký tự được biết là an toàn (danh sách trắng).

Chỉ cho phép các nhân vật có chức năng mà bạn biết, theo cách đó sẽ ít có nguy cơ thiếu thứ gì đó. Kết quả cuối cùng có thể là bạn chỉ quyết định cho phép [a-zA-Z0-9_], nhưng điều đó chỉ có thể là đủ để hoàn thành công việc. Bạn cũng có thể muốn kiểm tra xem ngôn ngữ và bộ công cụ của bạn không bao gồm các chữ cái có dấu như äötrong đó. Chúng có thể không được coi là đặc biệt bởi bất kỳ vỏ nào, nhưng một lần nữa, tốt hơn là chắc chắn nếu chúng vượt qua hay không.


1
Và đảm bảo bất cứ điều gì bạn sử dụng để đối sánh [a-zA-Z0-9]không bao gồm những thứ như àmã hóa của họ cũng có thể bị hiểu sai bởi một số shell (như bash) ở một số địa phương.
Stéphane Chazelas

@ StéphaneChazelas (không quan tâm :) có phải họ (và chỉ dưới những địa phương bị ảnh hưởng không?)
Wilf

10

Trong một ngôn ngữ lập trình, nên có nhiều cách tốt hơn để thực hiện hơn là thực thi các lệnh shell. Ví dụ: thay thế cd varbằng ngôn ngữ lập trình tương đương của chdir (var);bạn sẽ đảm bảo rằng mọi thủ thuật có giá trị làvar chỉ dẫn đến lỗi "Không tìm thấy thư mục" thay vì các hành động vô ý và có thể gây hại.

Ngoài ra, bạn có thể sử dụng đường dẫn tuyệt đối thay vì thay đổi thư mục. Chỉ cần ghép tên thư mục, dấu gạch chéo và tên tệp bạn muốn sử dụng.

Trong C, tôi có thể làm một cái gì đó như:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Chắc chắn ngôn ngữ lập trình của bạn có thể làm một cái gì đó tương tự?


Cảm ơn câu trả lời của bạn, nhưng thật không may, tôi phải sử dụng cách giải quyết với các lệnh bash. Tôi đã thử nghiệm trích dẫn biến - như muru đề xuất - và nó dường như hoạt động!
ES___
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.