2015-02-16 10 views
19

Tôi đã đọc khoảng CycriptCydia Substrate và cách chúng có thể được sử dụng cho các cuộc tấn công tiêm mã trên ứng dụng iOS. Mã như thế này sẽ khiến bạn sợ nếu bạn đang làm việc trong môi trường bảo mật cao. (Bỏ qua/etc/password phần, chỉ cần xem xét khả năng thay thế originalMessage với crackedMessage.)Swift có dễ bị tiêm mã không?

cy# MS.hookFunction(fopen, function(path, mode) { 
cy>  if (path == "/etc/passwd") 
cy>   path = "/var/passwd-fake"; 
cy>  var file = (*oldf)(path, mode); 
cy>  log.push([path, mode, file]); 
cy>  return file; 
cy> }, oldf) 

Tôi đọc một blog (mà tôi không lưu) mà nói rằng Swift không phải như dễ bị tấn công như Objective-C vì nó không phải là động. Sau đó, một lần nữa, tôi cũng đọc rằng bạn có thể làm method swizzling in Swift vì vậy nó không phải là rõ ràng với tôi nếu Swift cung cấp bất kỳ sự bảo vệ chống lại các cuộc tấn công tiêm mã.

Vì vậy, Swift có dễ bị tấn công bằng cách tiêm mã không?

Trả lời

36

Cuối cùng, không có cách nào để ngăn ai đó xâm nhập chương trình của bạn nếu bạn để chương trình chạy trên thiết bị của họ. Có nhiều cách để làm cho nó khó khăn hơn, nhưng không có cách nào để làm cho nó không thể.

tôi có thể nghĩ ra những cách quan trọng của tiêm mã vào một ứng dụng:

  • swizzling phương pháp Objective-C với thời gian chạy;
  • swizzling các phương thức Swift ảo bằng cách phân tích cú pháp thực thi và tìm ra các bit phù hợp để thay đổi;
  • sửa đổi mục tiêu cuộc gọi;
  • nhập các biểu tượng chuyển đổi bằng cách thay đổi các mục tiêu biểu tượng gốc;
  • sử dụng dyld để tải thư viện hoặc thay đổi thư viện mà chương trình của bạn tải;
  • thay thế các thư viện mà chương trình của bạn liên kết.

Và không có cách hiệu quả 100% để ngăn chặn bất kỳ điều này trong môi trường mà người dùng kiểm soát hoàn toàn. Bạn nên quyết định có nên lo lắng hay không tùy thuộc vào mô hình mối đe dọa của bạn.

Swizzling phương pháp Objective-C với thời gian chạy

Phương pháp swizzling là một kỹ thuật mà bạn thay đổi việc thực hiện một phương pháp trong thời gian chạy với tùy ý, đoạn mã khác nhau (thường là cho một mục đích khác nhau). Các trường hợp sử dụng phổ biến là bỏ qua kiểm tra hoặc tham số ghi nhật ký.

Sự gian lận trong Mục tiêu-C là một điều rất lớn vì thời gian chạy cần siêu dữ liệu xác định mọi phương thức và mọi trường mẫu. Tôi không biết bất kỳ ngôn ngữ nào khác có thể biên dịch thành mã máy gốc và giữ nhiều siêu dữ liệu này xung quanh. Nếu bạn có một cái gì đó như -[AccessControl validatePassword:], bạn chỉ làm cho nó thực sự dễ dàng cho những kẻ xấu. Với method_setImplementation, đây chỉ là việc xin lỗi xảy ra.

Vì các lớp Swift có thể kế thừa từ các lớp Objective-C, đây vẫn là thứ cần tìm. Tuy nhiên, các phương thức mới trên các lớp thừa hưởng từ lớp Objective-C chỉ tiếp xúc với thời gian chạy Objective-C nếu chúng có thuộc tính @objc (hoặc nếu lớp đó có thuộc tính @objc), do đó, nó giới hạn bề mặt tấn công so với mục tiêu -C.

Ngoài ra, trình biên dịch Swift có thể bỏ qua thời gian chạy Objective-C để gọi, phương thức devirtualize hoặc inline Swift không được đánh dấu dynamic, ngay cả khi chúng được đánh dấu @objc.Điều này có nghĩa rằng trong một số trường hợp, sự gian lận có thể chỉ xảy ra đối với các cuộc gọi được gửi qua Objective-C.

Và tất nhiên, hoàn toàn không thể nếu lớp hoặc phương pháp của bạn không được tiếp xúc với thời gian chạy Objective-C.

Swizzling phương pháp Swift ảo bằng cách phân tích ra thực thi và tìm các bit quyền thay đổi

Tuy nhiên, bạn không cần thời gian chạy Objective-C để trao đổi việc triển khai phương pháp. Swift vẫn có các bảng ảo cho các phương thức ảo của nó và vào tháng 2 năm 2015, chúng nằm trong phân đoạn __DATA của tệp thực thi. Nó có thể ghi được, vì vậy nó có thể làm cho các phương thức ảo Swift trở nên rối loạn nếu bạn có thể tìm ra các bit đúng để thay đổi. Không có API thuận tiện cho việc này.

Các lớp C++ có thể được sửa đổi tương tự, nhưng các phương thức Swift được ảo theo mặc định, bề mặt tấn công lớn hơn nhiều. Trình biên dịch được phép các phương thức ảo hóa như là một tối ưu hóa nếu nó không tìm thấy ghi đè, nhưng dựa vào tối ưu hóa trình biên dịch như là một tính năng bảo mật không chịu trách nhiệm.

Theo mặc định, các tệp thi hành Swift được triển khai là strip ped. Thông tin cho các ký hiệu không phải là public/open bị hủy bỏ, và điều này làm cho việc xác định các biểu tượng mà bạn muốn thay đổi này khó hơn nhiều so với Objective-C. Các biểu tượng Public/open không bị tước bỏ vì giả định rằng các máy khách mã bên ngoài khác có thể cần chúng.

Tuy nhiên, nếu ai đó tìm ra triển khai chức năng nào họ muốn trao đổi, tất cả những gì họ phải làm là viết địa chỉ của triển khai mới vào đúng vùng bảng ảo. Họ có lẽ sẽ cần phải làm cho trình phân tích cú pháp Mach-O của riêng họ, nhưng điều này chắc chắn không nằm ngoài phạm vi của những người tạo ra những thứ như Cycript.

Cuối cùng, các phương pháp final giảm nguy cơ này vì trình biên dịch không cần phải gọi chúng thông qua vtable. Ngoài ra, struct các phương pháp không bao giờ là ảo.

gọi Sửa nhắm

Nếu vẫn thất bại, kẻ tấn công của bạn vẫn có thể đi bộ qua mã máy tính của bạn và thay đổi bl hoặc call toán hạng hướng dẫn để bất cứ nơi nào họ muốn tốt hơn. Điều này có liên quan nhiều hơn và khá khó khăn/không thể nhận được 100% ngay với một phương pháp tự động, đặc biệt là nếu các biểu tượng bị thiếu, nhưng ai đó đã xác định đủ sẽ có thể làm điều đó. Bạn quyết định nếu ai đó cuối cùng sẽ tìm thấy nó có giá trị những rắc rối để làm điều đó cho ứng dụng của bạn.

Điều này phù hợp với các phương pháp ảo và phi ảo. Đó là, tuy nhiên, vô cùng khó khăn để làm khi trình biên dịch inlines cuộc gọi.

Swizzling ký nhập khẩu bằng cách thay đổi biểu tượng các mục tiêu còn sơ khai

Bất kỳ biểu tượng nhập khẩu, bất kể ngôn ngữ nó được viết bằng ngôn ngữ và nó đang được sử dụng từ, dễ bị swizzling. Điều này là do các ký hiệu bên ngoài bị ràng buộc trong thời gian chạy. Bất cứ khi nào bạn sử dụng một hàm từ một thư viện bên ngoài, trình biên dịch tạo ra một mục nhập trong một bảng tra cứu. Đây là một ví dụ về những gì một cuộc gọi đến fopen có thể trông giống như nếu bạn quay trở lại thực thi của bạn để mã C:

FILE* locate_fopen(const char* a, const char* b) { 
    fopen_stub = dyld->locate("fopen"); // find actual fopen and replace stub pointer to it 
    return fopen_stub(a, b); 
} 

FILE* (*fopen_stub)(const char*, const char*) = &locate_fopen; 

int main() { 
    FILE* x = fopen_stub("hello.txt", "r"); 
} 

Cuộc gọi ban đầu để fopen_stub thấy thực tế fopen, và thay thế địa chỉ trỏ đến bởi fopen_stub với nó.Bằng cách đó, dyld không cần phải giải quyết hàng nghìn biểu tượng bên ngoài được sử dụng từ chương trình của bạn và các thư viện của nó trước khi nó bắt đầu chạy. Tuy nhiên, điều này có nghĩa là kẻ tấn công có thể thay thế fopen_stub bằng địa chỉ của bất kỳ chức năng nào mà họ muốn gọi thay thế. Đây là ví dụ Cycript của bạn.

Viết ngắn trình liên kết và liên kết động của riêng bạn, bảo vệ duy nhất của bạn chống lại loại tấn công này là không sử dụng thư viện hoặc khung công tác được chia sẻ. Đây không phải là một giải pháp khả thi trong một môi trường phát triển hiện đại, vì vậy bạn có thể sẽ phải đối phó với nó.

Có thể có nhiều cách để đảm bảo rằng các trang web sẽ đến nơi bạn mong đợi, nhưng nó sẽ là loại không ổn định và những kiểm tra này luôn có thể là nop bị kẻ tấn công xác định. Ngoài ra, bạn sẽ không thể chèn các kiểm tra này trước khi các thư viện được chia sẻ mà bạn không có quyền kiểm soát các ký hiệu được nhập khẩu cuộc gọi. Những kiểm tra này cũng sẽ vô dụng nếu kẻ tấn công quyết định chỉ thay thế thư viện được chia sẻ với một thư viện mà họ kiểm soát.

Ngoài ra, các bao đóng khởi động cho phép dyld 3 thay thế các bảng tra cứu này bằng thông tin được gắn trước. Tôi không nghĩ rằng việc đóng cửa khởi chạy hiện chỉ đọc, nhưng có vẻ như cuối cùng chúng cũng có thể. Nếu có, thì các biểu tượng nổi bật sẽ trở nên khó khăn hơn.

Sử dụng dyld để buộc tải thư viện hoặc thay đổi mà các thư viện chương trình tải của bạn

Dyld supports lực lượng nạp các thư viện vào thực thi của bạn. Khả năng này có thể được sử dụng để thay thế bất kỳ biểu tượng đã nhập nào mà tệp thực thi của bạn sử dụng. Không thích bình thường fopen? Viết dylib để xác định lại nó!

Dyld sẽ không hợp tác với phương pháp này nếu tệp thực thi được đánh dấu là bị hạn chế. Có three ways để đạt được tình trạng này (tìm pruneEnvironmentVariables):

  • cho phép các bit setuid hoặc các bit setgid trên thực thi của bạn;
  • được ký mã và có quyền hạn "Chỉ bị hạn chế" OS X;
  • có một phần được gọi là __restrict trong một đoạn được gọi là __RESTRICT.

Bạn có thể tạo các __restrict phần và __RESTRICT phân khúc sử dụng sau "Flags Linker khác":

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null 

Lưu ý rằng tất cả trong số này là khá dễ dàng để phá vỡ. Các bit setuid và setgid là tầm thường để xóa khi người dùng điều khiển môi trường thực thi, một chữ ký mã dễ xóa, và phần hoặc phân khúc chỉ cần được đổi tên để loại bỏ trạng thái bị hạn chế.

Thay thế các thư viện liên kết mà chương trình của bạn chống lại

Nếu vẫn thất bại, một kẻ tấn công vẫn có thể thay thế các thư viện chia sẻ rằng thực thi của bạn sử dụng để làm cho nó làm những gì họ thích. Bạn không kiểm soát được điều đó.

tl; dr

Tiêm mã trong một ứng dụng Swift là khó hơn đó là cho một ứng dụng Objective-C, nhưng nó vẫn còn có thể. Hầu hết các phương pháp có thể được sử dụng để tiêm mã là độc lập với ngôn ngữ, có nghĩa là không có ngôn ngữ nào sẽ giúp bạn an toàn hơn.

Đối với hầu hết các phần, bạn không thể làm gì để tự bảo vệ mình chống lại điều này. Miễn là người dùng điều khiển môi trường thực thi, mã của bạn đang chạy với tư cách là khách trên hệ thống của họ và họ có thể thực hiện hầu hết mọi thứ họ muốn với nó.

+0

cập nhật tuyệt vời câu trả lời gốc của bạn! Cám ơn rất nhiều. –

0

Bạn đang nói về việc tiêm mã trên thiết bị iOS đã bẻ khóa. Rất đơn giản: Người dùng đã loại bỏ bảo vệ hệ điều hành của họ, vì vậy bây giờ bất cứ điều gì đi. Không an toàn. Nếu người dùng không tự nguyện loại bỏ sự bảo vệ đó, thì việc đi vào không gian địa chỉ của ứng dụng là không thể.

Các vấn đề liên quan