Mở đầu
Đó là vào một ngày nóng nực, nắng rải khắp bầu trời miền Trung, nóng đến nỗi mây cũng đi trốn, gió cũng đi trốn, chỉ còn mình trơ trọi. Nằm lướt Facebook, thấy cô bạn đăng một bức ảnh: Đọc rất buồn cười, giao dịch quốc tế mà có nhõi chưa đc $20, nhìn không thể tham được, thêm quả tên miền rất hề, "chất thải umion" à =)) Vào comment khen cô bạn cẩn thận, sẵn tiện tay vào cái link trên submit 1 acc trêu thằng scam, đại khái pass là "dcmmscamngudotcampuchiaak" cho nó tức chơi. Bỗng nãy ra ý định: "Sao phải comment tay khổ thế, sai cái thằng tool cho nó bơm vào ít rows chửi cho vui chứ tội gì, đang rảnh mà hehe"
Phân tích & xây dựng tool
Mở máy lên, trời nóng như thế này mà ngồi trước PC như cực hình anh em ạ! Đầu tiên, load cái trang web "chất thải uminon" (wasterumion) của tụi nó ra Ở đây trêu là trêu vậy, chứ bọn này cũng đã vận dụng kỹ thuật wrap words (westernunion -> wasterumion) để lừa những người không cẩn thận, không rành tiếng Anh, không rành naming branch nhé. Anh em chớ coi thường chỗ này, với tâm lý những người không rành tiếng Anh thì khi nhìn vào 1 từ tiếng Anh họ có xu hướng lướt nhanh qua chứ ko đọc trong đầu nên dễ sơ hở => Trick này ngày xưa anh em UG hay dùng để bypass black list address, đại khái là thêm 1 vài ký tự vô nghĩa vào address để đánh lừa Shipping services hoặc block list của website Thêm 1 quả đáng khen dành cho mấy thằng scam là dạo này chịu khó đầu tư, ít nhất là chăm chút được cái SSL certificate nên trông đỡ bẩn hơn rồi, anh có lời khen tới chúng mày !
Rồi, tiếp theo vào trang nhận tiền Tới đây, lại phải khen cái tụi này, dạo này lừa đảo được mùa nên cũng chịu khó đầu tư chăm chút, nâng cao trình độ, RIP cái giao diện web ra gì thật, tông màu rồi bài trí cái Phishing Page trông rất ra dáng, rất có tiềm năng lừa đảo đấy, anh có lời khen tới chúng mày lần nữa !
Thêm quả build cái list bank rất chu đáo, có mấy cái bank mà mình cũng chưa từng nghe bao giờ, xem ra tụi này rất khát máu, chơi hết !
Okay, nhìn sơ qua tới đây thì mình đoán được là anh em scam này sẽ lưu thông tin form này lại khi người dùng nhập vào, sau đó sẽ sử dụng thông tin này để đăng nhập vào tài khoản của người dùng và thực hiện hành vi đê hèn.
Fill ít thông tin, bấm Đăng nhập
Nó sang trang điền OTP
Tới đây mình hiểu đại khái kịch bản khả dĩ là như thế này:
- Thằng Scam gửi link tới victim, yêu cầu victim vào trang phishing page
- Victim nhập thông tin xong bấm "Đăng nhập"
- Lúc này, phishing page chuyển sang trang OTP (nhưng người dùng chưa có OTP để nhập)
- Thằng scam đọc thông tin ở form, nếu login đc vào bank info nó sẽ thực hiện trò hèn hạ, lúc này cần OTP của người dùng thì mã đc gửi tới điện thoại victim
- Victim lúc này đã có OTP, tưởng đâu sẽ nhận đc tiền sau khi nhập nên nhập OTP và boom ! Tiếng ai ca khúc oán cung sầu ngay !
Đấy, nên đại khái thằng scam này tạo cái page ra là làm như vậy, nên giờ chơi nó thì có nhiều cách:
- DDos nó => Ok nếu anh em có người nhà bị lừa, hay sẵn resource botnet, hay anh hùng hiệp nghĩa thì ngồi chơi thấy miẹ nó luôn
- Exploit site nó => Cái này trước có thấy trên group J2Team thì phải, anh em nào đó rảnh rảnh mò vào admincp của nó luôn haha, nhưng mình không tự tin lắm về khoản này ^^
- Code tool trêu nó => Cái này được này, chọn cách này nha !
Rồi, vậy chúng ta sẽ làm 1 tool thay con người (chính chúng ta) ngồi nhập data giả spam nó, vậy điều cần làm là xem cái request của nó gửi đi gồm những gì
Okay, nó sẽ gửi 1 Post request dạng Form data gồm có thông tin về mấy cái field ta điền (bank thì nó map theo id trong DB hay enum đại khái, nói chung nhìn con số đó nó suy ra đc bank nào) Còn username, password, tran_code (mã giao dịch) thì mình nhập tầm bậy á Quan trọng là cái token, cái đó chính là CSRF token
CSRF token (Cross-Site Request Forgery token) là một biện pháp bảo mật được sử dụng để bảo vệ bạn khỏi các cuộc tấn công lừa đảo mà trong đó người dùng không hề biết mình đã thực hiện một hành động trên một website. Chẳng hạn, một kẻ tấn công có thể lừa bạn nhấp vào một đường link xấu khi bạn đã đăng nhập vào tài khoản ngân hàng của mình, dẫn đến việc không may chuyển tiền mà bạn không hề biết.
Cách hoạt động của CSRF token:
- Tạo Token: Khi bạn truy cập vào một trang web có bảo mật, trang web sẽ tạo ra một chuỗi ký tự ngẫu nhiên gọi là "CSRF token". Chuỗi này là duy nhất và không thể đoán trước được.
- Gửi Token: Token này được gửi đến trình duyệt của bạn và thường được lưu dưới dạng một phần của form bạn cần điền trên trang web hoặc lưu trong cookie.
- Kiểm tra Token: Khi bạn gửi thông tin trở lại cho trang web (ví dụ: khi bạn đăng nhập, thay đổi mật khẩu, hoặc thực hiện giao dịch), trang web sẽ kiểm tra xem "CSRF token" này có khớp với token mà nó đã gửi cho bạn hay không. Nếu không khớp, yêu cầu sẽ bị từ chối vì có khả năng đó là một cuộc tấn công CSRF.
Cái này phải tìm cách lấy được từ page của nó, nhớ điểm này và tiếp tục
Trong Request Header và Response Header nó có trả về CSRF Token, tức là khi chúng ta request thì server nó trả về cookie cho trình duyệt, chúng ta sẽ dùng cặp token + cookie này trong Request để submit thành công Form về Server của chúng.
Okay, vậy câu hỏi đầu tiên, "Cái token gửi trong post body lấy ở đâu?" Chắc chắn là trong cái Form của nó khi gửi lên phải có, hãy inspect code Dễ dàng thấy ngay, nó là 1 input hidden trong Form, vì cái trang Phishing page này giao diện khá đơn giản nên ta phát hiện ngay Vậy là có 1 nửa của sự thật, nhưng 1 nửa của sự thật mãi ko phải là sự thật, ta phải kết hợp với cái Cookie, mà cookie thì có sẵn trong Response Header, vậy hình dung ngay cách chúng ta tạo nên sự thật từ 2 nửa sự thật =))
Đầu tiên, gọi 1 request lên trang Phishing page, sau đó lấy nội dung HTML trả về (response.Content), từ HTML đó ta trích xuất đc token, còn response.Headers thì chứa token (truy cập bằng key "Set-Cookie") Hàm C#, sử dụng RestSharp để làm điều đó
public static async Task<(string? token, string? cookie)> GetCookie(string apiUri) { var client = new RestClient(); var request = new RestRequest(apiUri, Method.Get); var response = await client.ExecuteAsync(request); var CSRFToken = Helper.ExtractCSRFToken(response.Content); var cookies = new StringBuilder(); for(int i = response.Headers.Count() - 1; i >= 0; i--) { if (response.Headers.ElementAt(i).Name == "Set-Cookie") { Console.WriteLine(response.Headers.ElementAt(i).Value.ToString()); var value = response.Headers.ElementAt(i).Value.ToString().Split(";")[0]; cookies.Append(value).Append("; "); } } return (CSRFToken, cookies.ToString().Remove(cookies.Length - 2)); } public static string? ExtractCSRFToken(string html)
{ if (string.IsNullOrWhiteSpace(html)) return null; var document = new HtmlDocument(); document.LoadHtml(html); var tokenElement = document.DocumentNode.SelectSingleNode("//input[@name='_token']"); if (tokenElement != null) { return tokenElement.GetAttributeValue("value", null); } return null;
}
Hàm trên sẽ dùng RestSharp gửi 1 request về Phishing Page, sau đó trích xuất Cookies và build lại Cookie string, tiếp theo khi có HTML content, sẽ dùng HTML Agility Pack để trích xuất token của Form, kết quả trả về là 1 Turple<string, string> chứa token và cookie string
Tiếp theo, ta sẽ dùng kết quả trên để build 1 POST request dạng multipart/form-data về Phishing Page
private static List<string[]> CreateRequestData(string token, BankEnum bank)
{ return new List<string[]> { new string[] {"_token", token}, new string[] {"bank", $"{(int)bank}"}, new string[] {"username", _random.Next() % 2 == 0 ? FileUtils.GetRandomLineFromFile(FileUtils.GetResourceFilePath(CommonConstants.VIETNAMESE_NAME_FILE_PATH)) : PhoneUtils.GenerateVietnamesePhoneNumber()}, new string[] {"password", FileUtils.GetRandomLineFromFile(FileUtils.GetResourceFilePath(CommonConstants.PASSWORD_DICTIONARY))}, new string[] {"tran_code", Utils.GenerateRandomTransactionNumberString()} };
}
Ở đây, mình trêu nó tới cùng, đã chơi là chơi cho đáng, không có bơm spam thông tin tầm bậy nó kỳ đi
- Mình submit token đã get đc ở phần trên
- Bank mình tạo ngẫu nhiên giá trị dựa trên giá trị list bank của tụi nó
- Tên đăng nhập mình build luôn 2 hàm: 1 gen số điện thoại ngẫu nhiên của các nhà mạng đang hoạt động, 1 tạo tên đăng nhập theo tên người Việt, cấu trúc tham khảo tên đăng nhập của acc sinh viên mình, cho nó real ! Hai cái này mình build sẵn file text để random dữ liệu mỗi lần đọc lấy 1 dòng ngẫu nhiên rồi sử dụng
- Tên thì mình lấy từ file tên của bạn Duyệt trên Github sau đó xử lý lại cho nó thành kiểu tên giống bank login, vd : "Nguyễn Trần Hoàng Linh" -> "nguyenthoanglinh"
- Password mình lấy từ file brute force password anh em hay xài, cho nó real
- Mã giao dịch mình gen luôn số ngẫu nhiên như bank hay Binance làm, vì trêu nó nên mình không cần quan tâm tới thuật toán, chỉ sinh dãy số ngẫu nhiên theo độ dài, lại cho nó real =))
Xong xuôi, mình set Cookie vào request
private static RestRequest BuildPostRequest(string apiUrl)
{ var request = new RestRequest(apiUrl, Method.Post) { AlwaysMultipartFormData = true, }; request.AddHeader("Cookie", Cookies); request.AddHeader("x-requested-with", "XMLHttpRequest"); return request;
}
Khi request thành công ta sẽ có response sau:
{ "code": 200, "html": "<form action=\"https:\/\/wasterumion.com\/update-tranfer-money-service-otp\" method=\"post\" class=\"form-validate\" absolute data-success=\"TRANSACTION_MANAGE.transactionDone\" accept-charset=\"utf8\" autocomplete=\"off\">\r\n <input type=\"hidden\" name=\"_token\" value=\"EZUfZsKnOQWF6cTKByLkwjc4Vdv9BzV93VufJWdm\"> <input type=\"hidden\" name=\"tran_info\" value=\"Nzc2\">\r\n <div class=\"item mb-5\">\r\n <p class=\"mb-2\">Qu\u00fd kh\u00e1ch vui l\u00f2ng nh\u1eadp m\u00e3 OTP \u0111\u1ec3 x\u00e1c nh\u1eadn giao d\u1ecbch t\u1ea1i \u0111\u00e2y<\/p>\r\n <input type=\"text\" class=\"w-full border border-[#a9a9a9] placeholder:text-[#c7c7c7] px-3 py-2 rounded-md focus:border-[#dbc32b] focus:shadow-md\" name=\"otp\" placeholder=\"Nh\u1eadp OTP...\" rules=\"required\" m-required=\"Vui l\u00f2ng nh\u1eadp OTP\" autocomplete=\"new-password\">\r\n <\/div>\r\n <button type=\"submit\" class=\"text-[black] bg-[#fd0] hover:bg-[#d9c64e] w-full py-2 px-3 rounded-md font-bold effect-real-btn transition-all\"><i class=\"fa-regular fa-paper-plane mr-1\"><\/i> G\u1eedi<\/button>\r\n<\/form>"
}
Các bạn chú ý, trong cái property "html" kia của chúng, ta thấy có cái "tran_info", đó là cái sẽ sử dụng để móc cái Form OTP này với cái Form Bank đã nhập khi nãy, đây là thứ cần để ta submit request tiếp theo trước khi finish trò chơi, viết hàm lấy nó ra nào
public class ResponseBankInfoModel
{ [JsonPropertyName("status")] public int Status { get; set; } [JsonPropertyName("html")] public string? Html { get; set; }
} ... if (response.IsSuccessful)
{ var result = JsonSerializer.Deserialize<ResponseBankInfoModel>(response.Content); string pattern = @"name=\""tran_info\"" value=\""([^\""]*)\"""; if (result?.Html is not null) { Match match = Regex.Match(result.Html, pattern); if (match.Success) { string tranInfoValue = match.Groups[1].Value; return (true, tranInfoValue); } } else { Console.WriteLine("Không tìm thấy giá trị 'tran_info'"); } return (false, "");
}
Dùng Json Parse nó ra Response model gồm 2 property, sau đó truy xuất property "HTML" để lấy code form OTP, sau đó xài Regex để trích thông tin ta cần ^^ Kết quả có được tran_info, build request tiếp theo nào
var requestOtpData = new List<string[]> { new string[] {"_token", token}, new string[] {"trans_info", tranInfo}, new string[] {"otp", Utils.GenerateRandomOtpNumberString()} }; bool otpResult = await RestSharpClient.SendOTPRequest("https://wasterumion.com/update-tranfer-money-service-otp", requestOtpData);
Mình tiếp tục tạo 1 dãy OTP 6 số như thật cho tụi này vui đùa chơi
Thật ra bước OTP này mình nghĩ sẽ vô dụng nếu như nó chưa đăng nhập mà OTP đã có, nhưng mình cứ bơm cho tròn câu chuyện luôn
Tới đây là đã xong câu chuyện cốt lõi, request đã thực thi xong, mình chỉ việc chạy Parallel để gửi 1000, 2000, 5000 request về CSDL của nó, ít nhất cũng làm trôi đi 1 số thông tin thật của người bị lừa khác. Nếu bơm nhiều quá nó sinh nghi thì lâu lâu lướt FB mình bơm cho nó 5,10 cái cho nó ngồi check chơi, chán thì treo tool trên mấy con VPS cũng đc ^^
Vậy, câu chuyện tiếp theo, chúng nó sẽ làm gì để đối phó với mình? Hãy suy nghĩ như chúng nó, nếu gặp trường hợp bị spam thì mình sẽ làm gì:
- Kiểm tra thông tin spam, nếu thông tin ngẫu nhiên, vô nghĩa thì sẽ bypass hoặc viết code lọc kết quả VD: bỏ các kết quả username chỉ có 1,2 ký tự, username là GUID, username có quá nhiều lần trùng nhau thì distinct, tương tự với password. => Để tránh trò đó, mình đã gen thông tin sao cho thật nhất có thể : username là số điện thoại, username là tên người không dấu có số như : nguyenthoanglinh1, password thì sử dụng list như ai )
- Chúng sẽ detect IP request và có thể ignore các request từ IP của mình => Mình sẽ thực hiện request thông qua Proxy, như vậy vẫn bơm nó đc
- Chúng sẽ nhúng Captcha để chặn mình => Môn này cũng hay, nhưng vẫn có cách chơi nó đc, tốn kém tí nếu muốn khô máu thôi ^^, nhưng nếu dính captcha thì có thể làm người dùng chậm hơn trong việc nhập thông tin, có thời gian suy nghĩ lại, nói chung chúng apply Recaptcha thì mình tính tiếp, giải pháp của mình sẽ là dùng 3rd service như Two Captcha để solve, tốn vài đô để chọc nó coi như uống cafe, cũng như việc mua proxy để chơi nó thôi.
Tóm lại, thông qua bài viết này, chúng ta có thể biết được cách tìm hiểu và giả lập lại một request gửi về server của victim và bơm thông tin spam để trêu chúng nó, qua đó hiểu được cách tấn công thì mình sẽ xây dựng phương án phòng thủ tương ứng. Đại khái muốn Phòng chống nghệ thuật hắc ám thì phải biết nghệ thuật hắc ám ^^
Mã nguồn
Project Tool: https://github.com/eddydn/FukScamV1
Credit
Thank to Duyet for Vietnamese name Json, 1000000-password-seclists text