ACSC 2021 | Web Writeup | API

 

API

Thử thách:

Kiến thức nền:

  • Broken Access Control.

Giải quyết vấn đề:

1/ Thăm dò:

Như mọi dạng bài cho source code từ trước, việc đầu tiên chúng ta cần làm là tôn trọng tác giả, mở source code ra đọc và deploy nó lên. Vào trong root folder public rồi deploy trên locahost bằng lệnh php -S localhost:<any port number>. Tạm thời chưa quan tâm đến các file config như 000-default.conf, docker-compose.yml hay Dockerfile, chúng ta sẽ sử dụng sau. Nhìn sơ qua thì chúng ta có một cái web app chỉ có 3 chức năng thế này:

  • Sign in:

image

  • Sign up:

image

  • Trang admin (không hiểu sao không cần sign in cũng vào được, nhưng mà nhìn chung nó cũng vô dụng):

image

Thử tạo một account tại signup.html rồi đăng nhập vào thử:

image

2/ Nghiên cứu source code:

Bài này nhìn chung là khá dễ, nếu thậm chí nếu bạn đọc code và suy nghĩ theo cách đơn giản thì sẽ ra flag cực kì nhanh. Nhưng tôi và thằng teammate baolongv3 đã chọn cách khó hơn, đó là ăn hết tất cả cú lừa của bài này.

Cú lừa đầu tiên, không biết là vô tình hay cố ý mà tác giả lại để lộ 2 cái file "mới nhìn tưởng là quan trọng và là chìa khóa để tìm ra flag" này:

  • user.db: file chứa toàn bộ thông tin account của tất cả các user trên web app này, mỗi field thông tin khác nhau được ngăn cách bởi dấu | (theo tôi dự đoán thì nó theo format sau: username|hash của password|user level (admin sẽ được gán bằng 1, normal user được gán bằng 0)). Khi refresh trang thì ta thấy file được append thêm một số account mới. Tất cả các account này đều có user level bằng 0. Chỉ duy nhất account có username tên Pang (trong hình) có user level bằng 1.

image

Nhìn vào hàm main, ta thấy file user.db vốn không có sẵn trong folder db. Nó được khởi tạo bằng cách gọi hàm gen_user_db:

function gen_user_db($acc){
    $path = dirname(__FILE__).DIRECTORY_SEPARATOR;
    $path .= "db".DIRECTORY_SEPARATOR;
    $path .= "user.db";
    if (file_exists($path)) return false;
    else {
        global $admin;
        $u = new User($acc);
        $fmt = sprintf("%s|%s|%d,", $admin['id'], $u->gen_hash($admin['pw']), $admin['level']);
        file_put_contents($path, $fmt);
    }
}

Nếu file user.db chưa được tạo, nó sẽ được tạo mới bởi hàm file_put_contents, đồng thời hàm này sẽ ghi vào file user.db mới được tạo account của admin thông qua biến $fmt, biến này lại lấy các field idpw và level từ global array $admin được gọi từ file config.php:

<?php
$admin = ['id' => "*secret*", 'pw' => "*secret*", 'level' => 1];
?>

Vậy là đúng như dự đoán, admin được gán level bằng 1, như vậy account có id là "Pang" chắc chắn là admin, và ta cần lấy được password của user này bằng cách dehash cái này: c307cae832059f15e52cc5e6a26a2eb3ae7173e6. Password được hash bằng hàm ripemd160:

public function gen_hash($val){
    return hash("ripemd160", $val);
}

Nhưng có vẻ đây là một challenge dùng não 100%, đừng phí thời gian chạy hashcat hàng tiếng đồng hồ để tìm password như tôi nhé, nó không ra cái gì đâu @@

  • passcode.db: chứa một string có độ dài 5 kí tự, nếu chỉ nhìn mà không đọc code kĩ thì sẽ rất dễ nhầm đây là salt mà tác giả ném vào hàm hash ripemd160 để băm password của các user. Nếu deploy đoạn code này (lấy từ hàm gen_pass_db) thì bạn sẽ đấy string này random từ biến $rand_str sau mỗi lần refresh trang:
$rand_str = "`~1234567890-=!@#$%^&*()_+qwertyuiopT[]\\";
$rand_str .= "asdfghjkl;':\"zxcvbnm./<>?QWERASDFZCVBNM";
$res = '';
for($i = 0; $i < $len; $i++){
    $res .= $rand_str[rand(0, strlen($rand_str)) - 1];
}
echo $res;

Nhưng trên url thì dù refresh lại bao nhiêu lần nó cũng ra ":<vNk". Lí do là vì file passcode.db cũng hoạt động giống file user.db, nếu file đã được tạo rồi thì hàm sẽ kết thúc và không đụng gì file nữa:

if (file_exists($path)) return false;

image

=> Xem xong file này tôi có 2 thắc mắc:

- Hai hàm gen_user_db và gen_pass_db đều hoạt động giống y hệt nhau, tại sao refresh trang user.db thì thấy các account mới được append vào còn passcode.db thì vẫn giữ nguyên như vậy? Chứng tỏ có một hàm nào đó khác nữa đã làm công việc append này, và nó chính là hàm signup (check file/2021/web/source/public/lib/User.class.php)):

public function signup(){
    if (!preg_match("/^[A-Z][0-9a-z]{3,15}$/", $this->acc[0])) return false;
    if (!preg_match("/^[A-Z][0-9A-Za-z]{8,15}$/", $this->acc[1])) return false;
    $data = $this->load_db();
    for($i = 0; $i < count($data); $i++){
        if ($data[$i][0] == $this->acc[0]) return false;
    }
    file_put_contents($this->db['path'], $this->db['fmt'], FILE_APPEND);  // $this->db['path'] == 'public/lib/db/user.db'$this->db['fmt'] = sprintf("%s|%s|%d,", $this->acc[0], $this->gen_hash($this->acc[1]), 0) 
    return true;
}

- Nếu ":<vNk" trong file passcode.db không phải là salt của hàm hash ripedm160, vậy nó được tạo ra để làm gì? Nhìn vào hàm is_pass_correct trong file Admin.class.php/2021/web/source/public/lib/Admin.class.php), ta thấy $passcode lấy data từ file passcode.db thông qua hàm get_pass, $input lấy data từ value của parameter pas được lưu trên superglobal REQUEST, sau đó nếu 2 biến này bằng nhau thì return true:

public function is_pass_correct(){
    $passcode = $this->get_pass();  // $passcode == ':<vNk'
    $input = $_REQUEST['pas'];
    if ($input == $passcode) return true;
}

3/ Khai thác:

  • Nói thêm một chút về các parameter nằm trong superglobal REQUEST của bài này, tất cả đều được gửi từ form signin thông qua hàm signin nằm trong file client.js/2021/web/source/public/static/js/client.js). Nếu theo luồng hoạt động của hàm này thì chỉ có 3 parameter được gửi vào REQUEST là idpw và c (1). Như vậy, để hàm is_pass_correct có thể return true, ta phải tự chèn thêm parameter pas=:<vNk vào sau khi sign in (2).
  • Như đã thấy ở phần thăm dò, dù có tạo được account thì chúng ta cũng không thể vào được bên trong, web app chỉ alert rằng "Only admin can access the page". Bắt thử một request rồi send qua repeater của Burp Suite thì ta được:

image

  • Response cho biết rằng trang web đang bị chuyển hướng đến /api.php?#access denied do đoạn code javascript location.href = '/api.php?#access denied';. Vậy đoạn code javascript này từ đâu ra. Check hàm main rồi mò lại hàm challenge, ta có:
$admin = new Admin();
if (!$admin->is_admin()) $admin->redirect('/api.php?#access denied');
$cmd = $_REQUEST['c2'];
if ($cmd) {
    switch($cmd){
        case "gu":
            echo json_encode($admin->export_users());
            break;
        case "gd":
            echo json_encode($admin->export_db($_REQUEST['db']));
            break;
        case "gp":
            echo json_encode($admin->get_pass());
            break;
        case "cf":
            echo json_encode($admin->compare_flag($_REQUEST['flag']));
            break;
    }
}
  • Đọc lướt qua thì ta sẽ thấy đây là một đoạn code authorize rất bình thường, khi account không phải admin thì sẽ trả về response như đã thấy trên Burp Suite. Nhưng nhìn kĩ lại một chút thì chúng ta phát hiện một sai lầm cực kì tai hại của người viết đoạn code này, đó chính là dùng if (!$admin->is_admin()) cho câu lệnh $admin->redirect('/api.php?#access denied'); nhưng lại quên đặt các khối lệnh phía sau vào else. Điều này đồng nghĩa rằng kể cả account của bạn không phải là admin, đăng nhập vào bị alert ra lỗi, nhưng vẫn có thể thực thi toàn bộ các lệnh ở phía sau if. Vấn đề bây giờ chỉ là chọn value nào để inject vào parameter c2 trong các value gugdgp và cf. Nếu chọn gd thì ta sẽ gọi được hàm export_db, hàm này lại lấy data từ paramter db. Cùng xem hàm này hoạt động như thế nào:
public function export_db($file){
    if ($this->is_pass_correct()) {
        $path = dirname(__FILE__).DIRECTORY_SEPARATOR;
        $path .= "db".DIRECTORY_SEPARATOR;
        $path .= $file;
        $data = file_get_contents($path);
        $data = explode(',', $data);
        $arr = [];
        for($i = 0; $i < count($data); $i++){
            $arr[] = explode('|', $data[$i]);
        }
        return $arr;
    }else 
        return "The passcode does not equal with your input.";
}
  • Để hàm này có thể chạy được ta cần phải chèn vào request pas=:<vNk như đã giải thích ở cuối phần Nghiên cứu source code. Trong luồng hoạt động của hàm này chúng ta chỉ cần để ý duy nhất lệnh $data = file_get_contents($path); là có thể kết luận sử dụng hàm này ta có thể đọc được nội dung của một file bất kì trong hệ thống, trong đó có file flag. Sau khi mò được đường dẫn của flag thì ta đã có flag trong tay:

image

  • Payload: id=Baictfkhoqua&pw=Aa1234567&c=i&pas=:<vNk&c2=gd&db=../../../../../../../flag
  • Flag: ACSC{it_is_hard_to_name_a_flag..isn't_it?}
spacer

Bí kíp 300 bài hack web thiếu nhi

Không có mô tả.

Internet ngày nay đã trở thành một phần không thể thiếu trong cuộc sống của mỗi chúng ta. Cùng với sự phát triển của internet, các website cũng ngày càng phức tạp hơn so với những phiên bản đầu tiên của nó vào những năm 1990. Website bao gồm 2 phần chính, là front-end (bộ mặt của website) và back-end(bộ não của website). Back-end là khu vực xử lý các thao tác với database (cơ sở dữ liệu), trả lại các giá trị dựa trên những request (yêu cầu) mà client (người dùng) gửi lên server. Vì back-end là nơi lưu trữ các dữ liệu quan trọng của người dùng một trang web như tài khoản, mật khẩu, thông tin cá nhân,... nó trở thành mục tiêu tấn công của các hacker, nhằm vào các thông tin nhạy cảm. Việc xây dựng một trang web cũng vô cùng phức tạp, nên các lỗ hổng bảo mật (vulnerability) xuất hiện là điều không thể tránh khỏi, dẫn tới sự xuất hiện của lĩnh vực “Web Exploitation” (khai thác ứng dụng web). Với tư cách là một sinh viên đang ngồi trên ghế nhà mình, học online tại FPTU, mình xin chia sẻ một vài hiểu biết của bản thân về một lĩnh vực rộng lớn và vô cùng quan trọng trong ngành bảo mật. Năm 2017, OWASP đã công bố 10 lỗ hổng nghiên trọng, phổ biến trên website như sau:

· Injection: Hậu quả của việc server nhận dữ liệu không được kiểm chứng và được biên dịch dưới dạng các câu lệnh hoặc các query (truy vấn trong cơ sở dữ liệu). Tiêu biểu phải kể đến SQL Injection, lỗi xuất hiện trên hệ cơ sở dữ liệu SQL. Lỗi này có thể giúp hacker xem, xóa, sửa, các thông tin trên hệ cơ sở dữ liệu. Bên cạnh đó chúng ta còn có LDAP injection và HTTP header injection. Tất cả các lỗ hổng trên đều dễ gặp phải ở những trang web của những lập trình viên mới vào nghề, còn ít kinh nghiệm (ngoại trừ trang web của tập đoàn B* nào đó 🐧 )

· Cross-Site Scripting XSS: Xảy ra khi hacker chèn mã độc (thường viết bằng ngôn ngữ JavaScript) thông qua các đoạn script để thực thi ở phía client. Thông thường, một đoạn mã độc được ngụy trang dưới dạng dữ liệu xuất nhập thông thường sẽ được gửi lên server. Nếu không có các bộ lọc dữ liệu độc hại, mã độc sẽ được chèn vào mã nguồn của ứng dụng web (ta thường gọi trang web đó đã bị XSS) và bất cứ lúc nào người dùng bình thường truy cập website, các mã độc đó sẽ thực thi ngay trên trình duyệt của người dùng.

· Broken Authentication: Ứng dụng web có thể cài đặt phần đăng nhập không chính xác, khiến cho hacker có thể chạy các trình brute-force nhằm tìm được chính xác tài khoản và mật khẩu của người dùng, dẫn tới việc chiếm tạm thời hoặc hoàn toàn tài khoản đó.

· Sensitive Data Exposure: Rất nhiều trang web và các API thường không bảo vệ các dữ liệu nhạy cảm của người dùng như thông tin tài chính, sức khỏe,... một cách hợp lý. Ví dụ, mật khẩu, tài khoản được mã hóa sơ sài trong cookies. Điều này dẫn tới các dữ liệu ấy có thể bị lấy được trong quá trình truyền dữ liệu từ client tới server.

· Broken Access Control: Lỗi này xảy ra khi có lỗ hổng trong việc hạn chế người dùng bình thường truy cập vào các vùng bị hạn chế. Kẻ tấn công có thể tìm thấy các lỗi này và truy cập vào các vùng bị cấm, chỉnh sửa thông tin người dùng, thay đổi quyền của admin, đánh cắp thông tin của toàn bộ hệ thống....

· XML External Entities (XXE)): Nói đơn giản, lỗ hổng này nhằm tới file XML (một loại ngôn ngữ đánh dấu) trên các ứng dụng web. Các file XML quá cũ hoặc được cài đặt kém sẽ là đối tượng tấn công, vì đã để lộ các thông tin như port, shared file, ... Hacker sẽ khai thác các thông tin này để scan port, thực thi code từ xa, hoặc thậm chí là DoS và DDoS.

· Security Misconfiguration: Đây là lỗ hổng thường gặp nhất và là hậu quả của việc sử dụng các cài đặt mặc định, vốn không an toàn trước những cuộc tấn công. Lỗi có thể xảy ra ở các kho lưu trữ đám mây mở, việc đặt HTTP header sai, hoặc các thông báo lỗi rườm rà sẽ chứa các thông tin nhạy cảm. Vì vậy, các hệ thống không chỉ cần được cài đặt một cách an toàn, chúng còn cần được cập nhật liên tục.

· Insecure Deserialization: Lỗ hổng này có thể dẫn tới việc thực thi các mã ở máy khách hàng. Dù việc này không dẫn tới việc thực thi mã độc từ xa, nó cũng có thể được dùng để tạo ra các cuộc tấn công, bao gồm tấn công lặp lại, tấn công kiểu injection, và tấn công leo thang đặc quyền.

· Using Components with Known Vulnerabilities: Xây dựng 1 website cần rất nhiều thành phần, bao gồm thư viện, framework, các module phần mềm, và chúng sẽ chạy với quyền tương tự như quyền của ứng dụng web đấy. Nếu một trong các thành phần đang chạy bị hack, hacker có thể lợi dụng quyền của nó để xâm nhập vào hệ thống.

· Insufficient Logging & Monitoring: Như tên gọi của lỗi này, ghi nhật ký và giám sát không đầy đủ, việc khai thác có thể gây nên các sự cố rất lớn. Kẻ tấn công dựa vào việc thiếu giám sát và phản ứng kịp thời để đạt được mục tiêu của chúng mà không bị phát hiện.

Nói chung, các lỗ hổng này thường được chia thành 3 mức độ nguy hiểm. Mức đầu tiên là các lỗ hổng gây ảnh hưởng tới 3 thuộc tính của dịch vụ là: Tính bảo mật, tính toàn vẹn và tính sẵn sàng. Nguy hiểm hơn là các lỗ hổng có thể gây nên việc thực thi mã từ xa. Và nguy hiểm nhất, chính là lỗ hổng gây nên các cuộc tấn công leo thang đặc quyền. Khi hacker thành công trong việc leo thang đặc quyền, hacker sẽ trở thành bố của hệ thống, có thể tùy ý tung hoành ngang dọc trong hệ thống mà không bị ai chặn lại, thậm chí có thể xóa luôn cả hệ thống ấy. Vì vậy, bên cạnh việc xây dựng giao diện thật đẹp, cũng như các tính năng hữu ích, việc bảo vệ hệ thống và tìm kiếm, nghiên cứu các lỗ hổng ứng dụng web mới luôn được đặt lên hàng đầu để tránh các hậu quả nặng nề.

Hãy theo dõi page của EHC để nhận thêm các thông tin, các tip hữu ích về an toàn thông tin trên mạng các bạn nhé !!!

spacer

An toàn thông tin (Information Assurance) 101

​ Trong một thời đại mà mọi quy trình nhàm chán đều có thể tự động hóa bằng những dòng code, số lỗ hổng bảo mật được phát hiện từ đó cũng tăng lên theo cấp số nhân, bởi lẽ không có một hệ thống, không có một sản phẩm được viết ra bằng code nào lại đảm bảo được sự an toàn tuyệt đối cả. Ngành An Toàn Thông Tin cũng vì đó mà trở thành một ngành cực kì rộng lớn với rất nhiều lĩnh vực. Với tư cách là một sinh viên vẫn đang còn ngồi trên ghế FPTU, mình chỉ xin chia sẻ về ngành IA trong giới hạn giảng dạy hiện nay của ngôi trường ba chữ. Nếu xét trên khía cạnh nghiên cứu hàn lâm, học thuật thì có thể chia ngành này thành 6 mảng. Các môn học được giảng dạy ở ngành IA tại FU cũng bám theo 6 mảng cốt lõi này:

  • Web Application Exploitation (Khai thác ứng dụng web), ứng với các môn IAW (Web Security) và HOD (Ethical Hacking and Offensive Security).

  • Reverse Engineering (Dịch ngược), ứng với môn IAM.

  • Binary Exploitation (Khai thác lỗi phần mềm), đây là một mảng khó, vẫn chưa được đưa vào giảng dạy.

  • Cryptography (Mật mã học), ứng với môn CRY.

  • Forensics (Điều tra chứng cứ số), ứng với môn FRS.

  • Networking Security (An toàn mạng), ứng với môn NWC.

    Trong series “An toàn thông tin 101” này, chúng tôi – FPTU Ethical Hackers Club sẽ cùng các bạn khai phá những vùng đất vô cùng mới lạ kể trên của ngành IA. Trước hết, hãy đảm bảo rằng các bạn đã chuẩn bị đầy đủ hành trang sau đây:

  • Một tâm hồn đẹp: khiêm tốn, đam mê khám phá, kiên trì, đó là 3 phẩm chất quan trọng để trở thành một “ethical hacker”. Đủ khiêm tốn để luôn luôn cảm thấy kiến thức của mình còn nhiều thiếu sót và không ngừng học hỏi. Đủ đam mê khám phá để luôn luôn cảm thấy tò mò khi nhìn thấy một dòng code, một tính năng của trang web, phần mềm. Và đủ kiên trì để ngồi hàng tiếng đồng hồ, hàng tuần và thậm chí là hàng tháng trời để đào sâu vào dòng code, vào tính năng đó cho đến khi tìm được lỗ hổng mới thôi.

  • Một vài kiến thức nền: lỗ hổng bảo mật không phải tự nhiên rơi từ trên trời xuống. Bạn phải có sự am hiểu nhất định về cơ chế hoạt động của đối tượng mà bạn đang tấn công. Sẽ dễ dàng và nhanh chóng hơn trong quá trình tìm hiểu nếu bạn đã có sẵn những kiến thức nền sau:

+ Lập trình: hãy bắt đầu từ những ngôn ngữ lập trình “học đường” như C, C++ hay Java để hiểu rõ những khái niệm cơ bản trong lập trình như biến, hàm, Structured Programming hay OOP, sau đó khi nhảy sang học các ngôn ngữ khác bạn sẽ cảm thấy rất dễ dàng. Một số môn học lập trình của ngành IA tại FPTU mà bạn cần lưu ý: PRF, PRO, PRP.

+ Hệ điều hành: học cách sử dụng những hệ điều hành mã nguồn mở như Linux cho phép bạn hiểu rõ hơn về bản chất và cấu trúc của hệ điều hành. Một số môn học về hệ điều hành của ngành IA tại FPTU mà bạn cần lưu ý: OSG, OSP

​ Ồ, bạn đã chuẩn bị hết mọi thứ rồi à? Vậy thì hãy nhấn like chiếc page này của bọn mình để bắt đầu hành trình đi tìm hiểu ngành An Toàn Thông Tin nào.

spacer