Using CodeQL and Semgrep to Assist Vulnerability Research (Part 2 of 6)

March 6th, 2025 by Brian

Work conducted by Huy Dai.

Introduction

For this blog series, I look to highlight the usage of CodeQL and Semgrep as complementary SAST (Static Application Security Testing) tools to improve automation in bug-hunting.

In the first blog post, I provided examples of a broadly generalizable bug query (written in both CodeQL and Semgrep) that were able to identify previously-known integer overflows CVEs across libraries such as libcurl, json-c, and libexpat. For that example, its simple construction and ability to operate across multiple codebases showcases the potential for CodeQL and Semgrep in their ability to function as scanning tools for high-level bugs.

However, another interesting use case for CodeQL and Semgrep is to write codebase-specific queries that focus only on vulnerable patterns in a given project. For example, once we identify a bug in one component of a program, we could try writing a CodeQL query/Semgrep rule for it to see if it appears in other components of the same program. Depending on the project, if one developer (or a team of developers) makes a mistake in one function, it could be very likely that there will be other, similar mistakes that would appear in other parts of the code.

In this section of the blog series, I want to highlight one such example of a codebase-specific query I wrote targeting a heap-based buffer overflow in BlueZ. While the query was originally intended to be a one-off query for BlueZ used as an exercise for data folow, I was able to later use it to find an unpatched bug in the library.

Targeting BlueZ CVE-2023-50229 (Heap-based buffer overflow)

Initially I was looking to write a query for CVE-2023-50229, a heap-based buffer overflow (7.1 HIGH) vulnerability in BlueZ, a very popular Bluetooth library.

In particular, I was interested in following code snippet:

Here, the data and len are user-controllable, where a user may be able to provide a property that is longer than 16 bytes. In such case, memcmp would read beyond the boundary pbap->primary or pbap->secondary for the bytes comparison (since the number of bytes is controlled by len), and in the likely case that they are different, the function will write out-of-bounds in memcpy! Thus, we are able to trigger a heap-based buffer overflow.

We can see in the patch commit that they avoid this issue by bounding the len variable:

There are a a few ways that you could go about trying to encapsulate this type of bug in CodeQL query or Semgrep rule. One possible root-cause explanation for this bug that len is a user-controllable variable but it is not bounded prior to being used as size parameter inside a memcmp and/or memcpy, which can lead to OOB read or OOB write.

Since I already had a CodeQL query related to the size paramter of memcpy being too large/unbounded, I decided to take a slightly different approach. In the following predicate, I captured the bug pattern as having a “misleading” memcmp call right before memcpy. Normally memcmp is meant to return whether two data buffers have the same value, but in the case that the size parameter of memcmp is too large and it reads out of bound, it will mostly likely always return 1 (not equal), and cause an unintended write.

Finding the Unpatched Bug

When I run this query on an affected BlueZ version, I was able able to flag the two vulnerable calls in read_version with CodeQL Vscode extension. In addition, I also get an unexpected result inside a function named read_databaseid:

It seems to sport the same exact vulnerability as in read_version! When checking out the original source file for this, I found that both functions are directly next to each other in bluez/obexd/client/pbap.c. In fact, they are both called by the same function, read_return_apparam:

However, as of the time of writing this blog, when navigating to the latest version of pbap.c in the BlueZ repository, we can see that the implementation of read_databaseid is still unpatched!

Looking Deeper into the History

So what’s happening here? Digging deeper into the BlueZ commit and CVE list, I found that there does seem to be a CVE entry that have been created for this bug named CVE-2023-51596. Despite it not mentioning any specific function name, this CVE entry has the exact same description as the first two found in read_version (e.g., CVE-2023-50229 and CVE-2023-50230):

BlueZ Phone Book Access Profile Heap-based Buffer Overflow Remote Code Execution Vulnerability. This vulnerability allows network-adjacent attackers to execute arbitrary code on affected installations of BlueZ. User interaction is required to exploit this vulnerability in that the target must connect to a malicious Bluetooth device. The specific flaw exists within the handling of the Phone Book Access profile. The issue results from the lack of proper validation of the length of user-supplied data prior to copying it to a fixed-length heap-based buffer. An attacker can leverage this vulnerability to execute code in the context of root.

And we know that the later two correspond to read_version because of this patch notice:

  • CVE-2023-50229: Fixed an out of bounds write in the primary version counter for the Phone Book Access Profile implementation .
  • CVE-2023-50230: Fixed an out of bounds write in the secondary version counter for the Phone Book Access Profile implementation .

Interestingly, the CVE number for the bug in read_databaseid is higher numerically than the ones in read_version, so it’s possible it was identified or verified much later and was subsequently not patched. As far as we could tell, there doesn’t seem to be any additional protections that would prevent a buffer overflow to protect the Database ID field that wouldn’t present on the Primary Counter or Secondary Counter.

Takeaways

For our finding with read_databaseid, the actual design of the CodeQL query was not as important, as the other “sister bugs” that existed in the codebase had the same exact pattern as the original in read_version. However, the important takeaway here is that by writing a CodeQL query that focused on a specific vulnerability identified in the project, I was able to quickly identify another bug that exists without having to manually look for it myself. This workflow help illustrates the idea that I proposed at the start of this section, which was to leverage CodeQL (and by extension, Semgrep), to allow us to find multiple project-specific bugs that normally doesn’t exist in other libraries.

It’s possible to imagine other real-world codebases where this pattern of iterative query writing and vulnerability research would come in handy. I envision a workflow where, as we learn more about a given codebase and get a feel for what the vulnerabilities look like, we could have a security engineer write patterns for them and scan through all code files to see if other similar instances exists.

Even if the pattern we wrote isn’t necessarily generalizable or applicable to many other projects, it can be a powerful strategy for leveraging existing knowledge about a given codebase to find more bugs – hence the added value for using SAST tools in our vulnerability research.

Looking Forward

So far I’ve provided examples of queries which I believe best showcase the potential in bug-hunting that we can achieve with CodeQL and Semgrep. In the following two sections of this blog (part 3 and 4), I will be going more in-depth into how to write CodeQL queries, specifically strategies on how to make your queries more generalizable and effective.