Lỗ hổng chuyển tiền hay là suy nghĩ như một hacker


Nhắc lại: trong một hội thảo gần đây, tôi có nói đầu năm 2017 tôi tìm được một lỗ hổng cho phép trộm tiền từ bất kỳ tài khoản nào của 4-5 ngân hàng ở Việt Nam.

Hình ở trên là một slide trong bài nói chuyện của tôi. Đây là một buổi giảng dạy về an ninh mạng, nên tôi dùng form này để hướng dẫn cho khách tham dự cách suy nghĩ như một hacker. Tôi thấy đây là một cách tiếp cận hiệu quả, mà không cần phải đi sâu vào các chi tiết kỹ thuật, bởi vì không phải ai nhìn vào ô số tiền cũng có thể nghĩ ngay đến "àh, tôi sẽ nhập vào số âm".

Lỗ hổng mà tôi phát hiện là có thật, nó không nằm ở chỗ số tiền chuyển khoản âm, mà là ở chỗ hoán đổi tài khoản của người nhận và người gửi. Một số bạn đọc thắc mắc, nghi ngờ về lổ hổng này. Tôi thấy nghi ngờ là đúng, chính tôi cũng không tin vào mắt mình khi tìm thấy lỗ hổng này.

Tôi không thể tiết lộ ngân hàng nào hoặc sản phẩm nào, vì lý do hiển nhiên, nhưng tôi có thể giải thích thêm về chi tiết kỹ thuật lỗ hổng khá thú vị này. Thiền sư gamma95 từng nói thật khó để thuyết phục người khác mía đường có vị ngọt, nếu họ chưa từng nếm thử bao giờ. Do đó, tin hay không thì tùy duyên vậy ;-).

---

Một giải pháp Mobile Banking thường bao gồm mobile app và một máy chủ API cung cấp các chức năng như truy vấn tài khoản hay chuyển tiền.

Câu hỏi: nếu được giao nhiệm vụ thiết kế API chuyển tiền trên máy chủ, API của bạn sẽ như thế nào?

Câu trả lời không khó, nhưng nếu chưa làm bao giờ thì cũng có nhiều thứ phải suy nghĩ. Trước khi xem tiếp, bạn hãy thử viết ra câu trả lời của riêng bạn.

Để chuyển tiền, máy chủ cần ít nhất 3 thông số:

1/ Tài khoản nhận

2/ Tài khoản gửi

3/ Số tiền

Tài khoản nhận và số tiền sẽ do người dùng nhập vào mobile app, rồi mobile app chuyển lên máy chủ. Nhưng tài khoản gửi thì lấy ở đâu ra?

Nếu cho phép người dùng chọn, lỡ họ nhập vào tài khoản của người khác thì sao? Tức là máy chủ phải kiểm tra người dùng có quyền chuyển tiền ra khỏi tài khoản đó hay không (authorization). Muốn như vậy thì trước tiên máy chủ phải xác thực người dùng (aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication).

Trên web, sau khi khách hàng đăng nhập, máy chủ sẽ trả về một HTTP cookie, browser ghi nhớ và sử dụng cookie để xác thực các yêu cầu tiếp cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365o. Thường các công nghệ làm web phổ biến (như .NET, Spring, PHP, hay Ruby On Rails) đã làm sẵn phần này, lập trình viên không cần phải làm gì thêm.

Vấn đề là trên mobile không có sẵn công nghệ nào như HTTP cookie, nên các lập trình viên thường phải tự chế ra cách xác thực. Có người sử dụng các giải pháp như OAuth hay JWT. Trong trường hợp này, sản phẩm dùng một giải pháp xác thực tự chế.

Trước khi nói về giải pháp đó, sẵn đang ở đây, tôi muốn nói thêm một chút về các giải pháp như cookie, OAuth, hay JWT. Tựu trung lại thì các giải pháp này đều giống nhau ở chỗ sau khi khách hàng đăng nhập, máy chủ sẽ cấp phát cho mobile app một token đại diện cho khách hàng. Mobile app sẽ đính kèm token khi gửi yêu cầu đến máy chủ. Máy chủ kiểm tra token như thế nào là mấu chốt của vấn đề.

Thí dụ thế này cho dễ hiểu nhé. Mỗi lần bạn gửi xe máy, nhà xe cho bạn một cái phiếu. Cái phiếu này có vai trò giống như token mà tôi nói ở trên. Muốn lấy lại xe, bạn phải đưa lại cái phiếu và nhà xe sẽ phải kiểm tra như sau:

1/ Cái phiếu còn hợp lệ. Như thế nào là hợp lệ thì tùy chỗ, thường thì họ sẽ dựa vào ngày tháng năm, con dấu, chữ ký ghi trên phiếu. Ví dụ như nếu nơi đó không gửi xe qua đêm mà bạn đưa phiếu ngày hôm qua thì tức là phiếu có vấn đề.

2/ Cái phiếu được phát cho chiếc xe mà bạn đang muốn lấy ra. Chỗ này quan trọng nha, vì nếu không, tôi có thể gửi vô một chiếc xe đạp, dùng phiếu của chiếc xe đạp để trộm một chiếc xe máy xịn. Để bịt lỗ hổng này, nhà xe thường ghi số phiếu lên yên xe. Khi bạn dắt xe ra, họ sẽ kiểm tra xem số trên phiếu và số trên yên xe có giống nhau không. Ai suy nghĩ như một hacker sẽ thấy ngay lỗ hổng: lỡ ai xóa số trên yên xe, ghi lại số khác thì sao? Giải pháp thường gặp là ghi số xe vào phiếu giữ xe. Khi bạn dắt xe ra, nhà xe sẽ kiểm tra số xe trên phiếu có trùng với số xe trên biển số hay không.

Quay trở lại câu chuyện thiết kế API chuyển tiền. Ngoài 3 thông số tài khoản người nhận, tài khoản người gửi và số tiền, mobile app cần phải đính kèm token của khách hàng. Máy chủ sẽ phải kiểm tra như sau:

1/ Token hợp lệ. Tương tự như phiếu gửi xe, token hợp lệ sẽ có, ví dụ như, chữ ký điện tử hợp lệ, còn hạn sử dụng, chưa bị thu hồi (revoked), v.v. Nhiều nơi sử dụng các giải pháp stateless như OAuth hay JWT, nhưng các giải pháp này có nhiều vấn đề. Vấn đề gì thì tôi muốn để dành làm bài tập về nhà cho bạn đọc.

2/ Token là của một người dùng và người đó được phép chuyển tiền ra khỏi tài khoản gửi. Trường hợp mỗi khách hàng chỉ có một tài khoản, trên token có thể ghi luôn số tài khoản đó (giống như trên phiếu gửi xe có ghi số xe). Trường hợp mỗi khách hàng có thể có nhiều tài khoản, trên token có thể ghi một user ID (là một số điện thoại chẳng hạn). Từ user ID này có thể lấy ra (từ core banking chẳng hạn) danh sách tài khoản mà người dùng có thể chuyển tiền ra. Máy chủ sẽ phải kiểm tra để đảm bảo số tài khoản gửi nằm trong danh sách này.

Tôi phát hiện máy chủ đã không thực hiện bước thứ 2. Nói nôm na, tôi sử dụng một token hợp lệ của chính tôi để vượt qua bước kiểm tra thứ 1, nhưng tôi đổi số tài khoản gửi bằng số tài khoản của nạn nhân mà tôi muốn trộm tiền. Trước khi chuyển tiền, máy chủ có yêu cầu phải xác thực OTP qua SMS. Nhưng, thay vì gửi OTP đến số điện thoại của tài khoản gửi, máy chủ lại gửi OTP đến số điện thoại nằm trong token, tức là số điện thoại của tôi.

Tôi viết là "hiểu nôm na" vì sản phẩm này không xác thực bằng token, mà dùng một giải pháp "nhà trồng được" khá rối rắm, nhưng có thể tóm tắt thế này:

1/ Giải pháp sử dụng chữ ký điện tử để xác thực khách hàng.

2/ Máy chủ tạo ra một bộ khóa RSA cho mỗi khách hàng.

3/ Mobile app dùng khóa riêng (private key) của khách hàng để ký tất cả các yêu cầu gửi đến máy chủ, kể cả các yêu cầu xác thực.

Câu hỏi hiển nhiên là: làm sao mobile app có được khóa riêng của khách hàng? Vì bộ khóa được tạo trên máy chủ (đây là một lỗ hổng khác), nên mobile app sẽ phải tải khóa riêng từ máy chủ. Ai có suy nghĩ như một hacker sẽ thấy ngay một vấn đề con gà và quả trứng ở đây: để xác thực thì cần phải có khóa riêng, nhưng để có khóa riêng thì cần phải xác thực trước đã!

"Giải pháp" mà họ sử dụng là thêm vào một API cho phép mobile app tải về khóa riêng của bất kỳ khách hàng nào, chỉ cần biết số điện thoại của khách hàng đó. Nói cách khác, đây là một unaucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated API giúp tôi có thể lấy khóa riêng và từ đó giả danh (impersonate) bất kỳ tài khoản nào.

Tóm lại thì giải pháp này chỉ an toàn khi người ta không hiểu được giao thức giữa máy chủ và mobile app. Đây là một sự vi phạm nguyên lý Kerckhoff khi thiết kế giao thức bảo mật.

Một hệ thống an toàn là một hệ thống mở, ai cũng biết cách thức nó hoạt động, nhưng không ai có thể phá vỡ nó. Nếu sự an toàn của hệ thống phụ thuộc vào giả định rằng không ai biết cách nó hoạt động ra sao, không sớm thì muộn hệ thống đó sẽ bị tấn công. Làm rối rắm mã hay che dấu cách thức hoạt động của phần mềm không phải là không có tác dụng trong việc tăng cường bảo mật cho hệ thống, nhưng chúng không thể là phương thức bảo vệ duy nhất. Dịch ngược mã phần mềm khó, nhưng rất nhiều người làm được.

Thiết kế của hệ thống cần phải là thiết kế mở vì chỉ như vậy mới chống lại được các hành vi phá hoại, xâm nhập từ bên trong, vốn là mối nguy lớn nhất cho các hệ thống ngân hàng. Rất nhiều nhân viên ngân hàng hiểu giao thức giữa mobile app và máy chủ, nếu những người này muốn trộm tiền, làm sao để ngăn chặn họ? Ngày hôm nay họ còn là nhân viên, nhưng nếu sau này họ nghĩ việc thì sao? Chỉ có thể trả lời được câu hỏi này bằng cách xây dựng hệ thống với giả định rằng kẻ tấn công đã biết rõ mã nguồn và tài liệu thiết kế.

Happy hacking!

---

Cập nhật: tôi muốn viết thêm một chút về thiết kế giải pháp an ninh mạng (security engineering) vì đề tài này liên quan đến tuyến bài chính về tình hình và chính sách an ninh mạng ở Việt Nam.

Thiết kế và xây dựng giải pháp xác thực cho một sản phẩm mobile banking là một công việc lai giữa applied security và product security. Việc này không dễ cũng không khó, nhưng tôi nghĩ ở Việt Nam chưa có mấy người làm. Đa số người làm security ở Việt Nam tập trung vào tìm kiếm lỗ hổng, hoặc network security, rất ít người có thể xây dựng giải pháp để giải quyết các vấn đề security thường gặp như identity access management bao gồm aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication, authorization và auditing, key/secret management hay data protection.

Những gì tôi viết ở trên, những câu hỏi tôi đặt ra chứa đựng nhiều gợi mở cho những ai mới bắt đầu trong lĩnh vực này. Nếu có chỗ khó hiểu, bạn hãy để lại còm, tôi sẽ tìm cách giải thích, nhưng tôi khuyên là nên đọc kỹ, suy ngẫm, rồi tự thiết kế một giải pháp xác thực, tìm cách phá vỡ nó và lặp lại. Đây là cách học tốt nhất.

Comments

Unknown said…
cảm ơn anh vì đã bỏ thời gian ra chia sẻ kiến thức ^^
J2TeamNNL said…
Cảm ơn anh, em học lập trình đọc rất dễ hiểu ạ

Đến từ J2Team Community
ha thanh said…
phần ngươi của bạn đã chiến thăng, chúc mừng bạn
TheMaster said…
Hay quá bạn ơi
Unknown said…
Cảm ơn anh Thái đã chia sẻ rất nhiều thông tin thú vị.
ho van tien said…
Cảm ơn anh về bài viết.
Twint app giải quyết đơn giản, ít nhất là đối với người dùng.
- Người gửi và nhận phải cài cùng ứng dụng phần client. Số tài khoản thanh toán và cách thức aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntification sẽ được xác định khi cài.
- Chỉ có thể chuyển trả chứ không thể lấy tiền của người nhận. Chỉ có thể gửi yêu cầu số tiền phải trả đến người nhận.
- Điện thoại của người nhận chọn qua danh sách "contact".
- Số tiền trả chuyển một lần bị giới hạn và giới hạn tổng số gửi một ngày.
- Không thể cho số âm vào số tiền trả. Quá cơ bản.
- Ba thông tin được mã hóa qua khóa tạo ra tự động và bị xóa khi tin đến mấy chủ.
- Quá trình kiểm tra, thanh toán được làm trên máy chủ. Chỉ có thông tin qua mã hóa giữa clients và máy chủ.
HaDzuong said…
Bài tập về nhà lần này khó quá Thái ơi :))
Nhưng bài viết này rất hay và nhiều giá trị chia sẻ. Nếu có demo nữa thì hay quá.
Unknown said…
Chào anh Thái,

Em không học chuyên về Security Engineering nhưng cũng rất hứng thú với nó. Em có tìm hiểu về nguyên lý làm việc của SSH, có chỗ nào chưa đúng anh Thái sửa giúp em:

"SSH tạo ra 1 cặp mã RSA (server giữ private-key, client giữ public-key) để mã hóa thông tin khi trao đổi session key (mục đích của session key là để mã hóa thông tin được trao đổi giữa client và server). Bên cạnh đó, để xác thực (aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication), client phải tạo ra 1 cặp mã RSA khác và gửi public-key này lên server và giữ lại private-key. Server sẽ dùng public-key mà client đã gửi lên để tiến hành xác thực."

Câu hỏi của em là: nếu cái Mobile App mà anh nói đến bắt chước cách hoạt động của SSH thì sẽ giải quyết được vấn đề bảo mật chứ?

Cảm ơn anh!
Thai Duong said…
>Câu hỏi của em là: nếu cái Mobile App mà anh nói đến bắt chước cách hoạt động của SSH thì sẽ giải quyết được vấn đề bảo mật chứ?

Ừ, đúng rồi, nhưng mà triển khai được giống như SSH cũng không đơn giản. Bạn có thể đọc https://crypto.stanford.edu/~dabo/cryptobook/ chương 18.
Thích nhất 2 đoạn cuối của bài viết và chắc có lẽ đúng là điều tác giả muốn gửi gắm, một giải pháp tổng thể và toàn diện về bảo mật cũng là một mặt trận của một cuộc chiến, giống như hải quân, không quân.... cần cả yếu tố chiến thuật chứ không đơn thuần là kỹ thuật thuần túy.
Unknown said…
Bài này ra đời cứ như là dự báo kiểu chiêm tinh, tiên tri vậy. Ngân hàng quân đội MB Bank bị lỗi gì đó, khách hàng thoải mái xài tiền chùa mà chưa cần đến hacker ra tay :D
foo123 said…
Câu hỏi hiển nhiên là: làm sao mobile app có được khóa riêng của khách hàng? Vì bộ khóa được tạo trên máy chủ (đây là một lỗ hổng khác), nên mobile app sẽ phải tải khóa riêng từ máy chủ. Ai có suy nghĩ như một hacker sẽ thấy ngay một vấn đề con gà và quả trứng ở đây: để xác thực thì cần phải có khóa riêng, nhưng để có khóa riêng thì cần phải xác thực trước đã!


==> Nếu điểm này là thật thì đội thiết kế của NH này thật sự có vấn đề. Private key có thể chia sẽ và lưu không phải tại 1 nơi
Unknown said…
Em chào anh. Thật sự em tìm anh để tìm 1 cơ hội dù là mong manh. Em muốn nhờ anh giúp đỡ. Em bị 1 người lừa đảo qua face book. Em đã chuyển cho họ 130 triệu. Là 1 số tiền lớn đối với gia đình em. Em chỉ biết duy nhất face book của họ với thông tin không biết có phải thật hay không. Có cách nào để tìm được sự thật người này là ai không ạ. Theo em biết thì người này cũng giỏi về công nghệ thông tin khi hack tài khoản face của người khác. Nếu anh đọc được comment của em xin anh giúp đỡ em. Gmail của em là bachhop784533@gmail.com.
Thai Duong said…
Unknown: bạn email chi tiết sự việc đến thaidn@gmail.com. Tôi không chắc sẽ giúp được gì, nhưng tôi sẽ cố gắng.
PNTuser said…
Bài viết rất hay, đọc xong đc mở mang nhiều kiến thức, cám ơn anh rất nhiều.
Unknown said…
Em chào anh, đọc bài viết của anh em hiểu ra được nhiều điều, trước làm mobile app em thường thiết kế thuật toán và viết nó dưới C++ build ra thư viện .so, hoặc .a, mọi key mã hóa đều được em lưu trong đó. Liệu giải pháp của em có an toàn không và liệu các thư viện so có thể dịch ngươc được không anh
Thai Duong said…
>Em chào anh, đọc bài viết của anh em hiểu ra được nhiều điều, trước làm mobile app em thường thiết kế thuật toán và viết nó dưới C++ build ra thư viện .so, hoặc .a, mọi key mã hóa đều được em lưu trong đó. Liệu giải pháp của em có an toàn không và liệu các thư viện so có thể dịch ngươc được không anh

Không an toàn nhé bạn, dịch ngược được hết bạn ơi. Nhiều khi chẳng cần dịch ngược gì, chỉ cần strings là ra luôn key mã hóa.
thanhmeou said…
Chào anh Thái, nay tôi chuyển tiền trên 1 Ngân Hàng của Việt nam rât lớn, tôi thấy có 1 lỗi là khi chuyển tiền VND nó chuyển qua USD (ví dụ thay vì chuyển 100trieu VND, nó thành 100trieu USD). Cả tin nhắn OTP để thể hiện là 100trieu USD.
x2pi3141 said…
Thiền sư gamma95 là nhân vật hư cấu phải không ạ?