[Quick note] How to build CodeQL DB with closed-source project(.NET Assembly)
Disclaimer: Bài chia sẻ chỉ phục vụ cho mục đích học tập, … mang tính chất cá nhân. Không liên quan tới bất cứ tổ chức cơ quan nào, bla bla … Tác giả không chịu trách nhiệm với bất kỳ hành vi nào của người đọc.
Mình bắt đầu viết bài này vào khoảng 1 năm trước (09/2023), tuy nhiên còn nhiều vấn đề khúc mắc về mặt pháp lý, bận rộn công việc, và chủ yếu là thiếu mood viết bài nên vẫn nằm trong draft cho tới tận bây giờ. Và do khoảng cách viết bài quá xa nên có thể sẽ có sự không đồng nhất giữa các phần, kính mong quý bạn đọc xem xét và giúp tác giả chỉnh sửa lại, xin cảm ơn!
# Linh tinh
Sau 3 năm làm đồ án về Semmle/CodeQL, tình cờ lần này làm luận văn cũng tiện dính dáng chút tới nên tranh thủ hoàn thiện nốt project còn dang dở năm nào.
Nói sơ qua về CodeQL,
Đại khái đây là một nền tảng để tìm kiếm các lỗ hổng tiềm tàng, biến thể hoặc những đoạn code chưa được tối ưu trong mã nguồn của bạn, nói ngắn gọn thì nó là một nền tảng rà quét mã nguồn!
Điểm khác biệt của CodeQL so với các công cụ rà quét mã nguồn khác đó là mọi thành phần của mã nguồn đều được “dữ liệu hóa”, việc scan mã nguồn là công đoạn sử dụng các QL — Query Language để truy cập tới các dữ liệu này, tương tự như cách sử dụng các hệ quản trị cơ sở dữ liệu SQL vậy.
Ví dụ đơn giản như sau:
Từ tất cả Class
tìm những class có tên = "Person"
Biểu diễn với CodeQL sẽ thành:
from Class clazz
where clazz.hasName("Person")
select clazz
Ngôn ngữ query của CodeQL rất tường minh, như vậy sẽ rất dễ tiếp cận cho những người mới, kể cả những người mới học code, những người chuyên về dữ liệu.
CodeQL còn mở miễn phí cho tất cả mọi người dùng sử dụng và đóng góp Query. Thậm chí nếu bạn đóng góp một Query có ích cho cộng đồng, bạn sẽ được nhận thưởng khoảng 1–2k$ bounty đến từ chính Github.
Việc vẫn hành nó có thể thực hiện một cách tự động hoặc bán tự động. Với tính năng enterprise, CodeQL có thể được tích hợp vào devops, để kiếm tra code quality, rà quét lỗ hổng …
Còn với license public, CodeQL cho phép researcher dùng với các project có mã nguồn mở.
Cũng do mục đích hoạt động của phiên bản CodeQL public là sử dụng với các dự án có mã nguồn mở nên khi tạo Database để query, bắt buộc toàn bộ mã nguồn của project đều có thể được biên dịch (hoặc thông dịch), chỉ cần một Exception nhỏ cũng sẽ ngừng cả quá trình tạo DB. Việc này ảnh hưởng rất lớn tới quá trình query sau này.
Có thể trong mã nguồn sử dụng tới thư viện của một bên thứ ba, trong quá trình tạo DB thì thông tin về nội dung của đoạn mã này sẽ không được thu thập, ví dụ như:
import java.util.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.map.LazyMap; //[1]
import org.apache.commons.lang.StringUtils;
private void createMap(){
Transformer reverseString = new Transformer( ) {
public Object transform( Object object ) {
String name = (String) object;
String reverse = StringUtils.reverse( name );
return reverse;
}
}
Map names = new HashMap( );
Map lazyNames = LazyMap.decorate( names, reverseString );//[2]
String name = (String) lazyNames.get( "Thomas" );
System.out.println( "Key: Thomas, Value: " + name );
}
Thư viện Apache Commons Collections được sử dụng, ở đây là LazyMap và Transformer. Trong quá trình CodeQL tạo database, sẽ chỉ thu thập được method createMap() gọi tới LazyMap.decorate(), chứ không biết được LazyMap.decorate() sẽ làm gì với dữ liệu.
Từ việc bộ dữ liệu đã bị thiếu sót như vậy dẫn tới việc query dữ liệu về sau trở nên thiếu chính xác. Sẽ có rất nhiều trường hợp mà khi đọc code chay thì thấy call graph tới được, nhưng khi query thì lại không có kết quả gì cả, và thậm chí còn không biết là call graph bị ngắt ở đâu ¯\_(ツ)_/¯. Đây chính là lý do mà mình cố gắng tìm cách để build được database cho cả những thư viện đi kèm, những file đã được compile rồi.
Hồi 2020–2021 mình có build thành công DB cho Java mà không cần mã nguồn, và đã chia sẻ tại đây, ý tưởng đơn giản chỉ là:
- decompile tất cả file jar
- tạo file build thử công với lệnh
javac
- và cuối cùng cho CodeQL tạo database với file build trên
Sau khi build thành công DB của một số products closed-source thường gặp thì mình cũng có thử học cách viết query để tìm bug. Tuy nhiên kích thước của DB khá lớn nên mỗi lần query tìm DataFlow gần như không bao giờ có kết quả trả về, thường là CodeQL bị đơ và crash luôn machine 🤣.
Đây cũng là một điểm yếu nữa của CodeQL cũng như các công cụ rà quét mã nguồn khác, khi mà kích thước DB lên quá lớn (tầm 300MB) thì việc query không còn hiệu quả nữa.
.
.
# Build CodeQL DB for a closed-source C# project
Tới dạo gần đây thì mình có làm lại một số project, cụ thể là luận văn tốt nghiệp, cũng có liên quan tới CodeQL và mảng C# nói riêng nên đành lật lại xem đó giờ Github có tối ưu thêm gì không.
Đối với C# thì việc build DB cho closed-source project khá dễ dàng, không yêu cầu cả project có thể build được (ví dụ như trường hợp thiếu dependency, …).
Do là đó giờ chưa có ai đăng (hoặc không dám đăng) guide để build DB cho closed-source C# project nên mình đành viết tạm vài step mình hay sử dụng để build.
Step 1: Collect DLL
Mình hay dùng dnSpy để collect, đầu tiên bấm vào tab Debug
> Attach to Proccess
rồi chọn process mà service đang chạy để debug, với các target như Exchange, SharePoint thì process này thường là w3wp.exe
Sau khi đã attach debugger thành công, tiếp tục vào Debug
> Windows
> Modules
Cửa sổ này sẽ list ra tất cả các managed dll (tạm thời hiểu là các dll được code bằng C#) mà process đã load vào memory. Tiếp tục Right click > chọn Open All Modules
, tất cả các dll này sẽ được load vào dnSpy
Step 2: Decompile all DLL to source code
Các dll vừa load sẽ được show trong cửa sổ Assembly Explorer. Tại đây Ctrl + A để chọn tất cả các DLL rồi chọn File
> Export to Project
Chọn version VS cho phù hợp, mình thì hay chọn VS2019 theo cảm tính, sau đó click Export và chờ thôi, có thể trong quá trình này sẽ hiện một số lỗi gì đó nhưng cứ ignore đi.
Step 3.0: Patch CodeQL
TLDR; với codeql mặc định, khi build database closed-source sẽ bị miss rất nhiều dependency, do đó ta phải thực hiện thêm step này
Tại thời điểm viết bài, mình sử dụng phiên bản codeql 2.13.3, phiên bản này không chính thức hỗ trợ việc build closed-source project, do đó ta phải sửa đổi một chút để có thể build.
Tool để build database csharp của codeql nằm tại: codeql\csharp\tools\win64\:
Trong đó, luồng xử lý build close sourced như sau:
codeql command line -> set env -> Semmle.Autobuild.CSharp.exe -> Semmle.Extraction.CSharp.Standalone.exe
Các file exe này được build với .NET Core 7.0, do đó file thực thi chính sẽ là các file .dll với tên tương ứng.
Ta có thể xác minh điều này bằng cách xem process tree khi build db với codeql.
Trong đó ta cần chú ý tới command line:
Semmle.Extraction.CSharp.Standalone.exe --references:.
Theo như phần usage của Semmle.Extraction.CSharp.Standalone.exe, ta có thể biết được option — references:
cho phép truyền thêm vào path của dependencies, phục vụ cho quá trình build.
Tuy nhiên, một điều trớ trêu ở đây là với phiên bản codeql mặc định, ta không thể customized option này thông qua codeql command line được, do option này đã bị hardcode tại: Semmle.Autobuild.CSharp.StandaloneBuildRule:
Mình giải quyết việc này bằng cách tạo thêm 1 file exe proxy thay thế Semmle.Extraction.CSharp.Standalone.exe gốc, đứng ở giữa làm nhiệm vụ inject thêm customized parameter, sau đó sẽ gửi tới file thật:
Semmle.Autobuild.CSharp.exe -> Semmle.Extraction.CSharp.Standalone Proxy -> Semmle.Extraction.CSharp.Standalone real
Sau khi replace file Semmle.Extraction.CSharp.Standalone.exe bằng file proxy thì ta có thể qua bước tiếp theo được rồi!
Step 3: Build C# DB
Mở cmd tại folder vừa export project ở trên, gõ lệnh build như sau:
codeql database create SPTestDB --language=csharp -Obuildless=true --overwrite
Với SPTestDB
là tên của CodeQL DB, option -Obuildless=true
cho phép build DB mà không cần phải compile thành công cả project.
Quá trình build sẽ tốn đâu đó khoảng 15–20p cho đến 1–2 ngày tùy vào độ lớn của project cũng như cấu hình máy của bạn, sau khi build xong thì DB sẽ được tạo tại folder chạy codeql:
Như vậy là ta đã build thành công một CodeQL DB mà không cần tới mã nguồn của project.
Tuy nhiên, do project rất nặng (~500MB đã nén) nên hãy query trên một máy tính có cấu hình cao một chút, đặc biệt là phải xem xét kỹ lưỡng từng query trước khi chạy, giới hạn tập dữ liệu query tối ưu nhất có thể.
Việc chạy query bừa bãi có thể khiến codeql query server bị hang/crash/not responding, đặc biệt là các query liên quan tới việc trace Data flow.
Dưới đây là một số query mẫu mình hay sử dụng với những db nặng như này:
- https://gist.github.com/testanull/b7c4dca00e287e5008943ece22ee3aa4
- https://gist.github.com/testanull/4c1d13a27c821d061c6191a53fa361a8
- https://gist.github.com/testanull/a9fa62dd29f0f128fcd6825f962daff5
Mình tìm thấy mấy cái này trong máy và đưa ra đây để làm ví dụ chứ tới thởi điểm hiện tại mình cũng ko nhớ chính xác nó dùng để làm gì nữa =)).
Nếu có thắc mắc về vấn đề này, bạn đọc vui lòng inbox telegram @testanull
, mình sẽ tìm hiểu lại cùng bạn!
Xin cảm ơn bạn đọc đã theo dõi tới đây!
__Jang__