← Back to list

How I got my first CVE in Rust

Disclaimer : Information written in this blog is my independent research 🥲. I am open for suggestions/corrections/collaboration/scolding…

Saksham Jain (RxW777) · 2024-04-02 05:30 · 6 claps · 3.6 min read
#rust #cybersecurity #cve #vulnerability #buffer-overflow
Open on Medium ↗
Wiki topics: 🔒 · Cybersecurity 📊 · Economic Policy

How I got my first CVE in Rust

Disclaimer : Information written in this blog is my independent research 🥲. I am open for suggestions/corrections/collaboration/scolding etc.

This blog is about CVE-2023–45544 (not published yet).

How it all started….

2 years ago, I started learning Rust because I just wanted to explore this new language, and I loved this language...😁 it was worth the learning curve. And I also like Linux (I don’t use Arch btw…).

In 2023, I saw the news that now Rust will be also used for Linux kernel development. I know that Linux was written in C lang, so that made me curious about how can we call functions from C libraries to Rust. I learned about a feature of Rust, FFI (Foreign Function Interface).

Rust FFI (Foreign Function Interface) is a feature that allows Rust code to call functions written in other programming languages, commonly C or C++ (i will also publish my learning on Rust FFI later…😅)

Demo

github link : https://github.com/Sjain-dir/Bug_in_Rust

In summary, when 2 arguments are passed to a C function from Rust, making changes in the first parameter affects the value of the second parameter.

So, I was casually learning about FFI, and I observed that when we have to call a C function in Rust, we first have to declare its parameter type in Rust.

Hmmm...🤔, what if I lie to rust about parameter type of the function? 🤨

In the code below, I am passing some values from Rust to C :

//Interface.c code
void test1(int* i) {
    printf("in test 1 :::\n");
    printf("i pointer value is : %p\n",i);
    printf("i -> value in that addressr is : %d\n",*i);
}

void test2(char* string1, int *i) {
    printf("in test 2 :::\n");
    printf("i pointer value is : %p\n",i);
    printf("i value in that address is : %d\n",*i);
    printf("string1 value is : %s\n",string1);
}

In test1, I took an int pointer, returned it’s address and value. In test2, I did the same, but here, I took a char pointer.

In main.rs

use std::ffi::{
    c_int,
    CString
};

#[link (name = "interface", kind = "static")]
extern "C" {
    fn test1(i : *mut c_int);
    fn test2(string1 : *mut CString, i : *mut c_int);
    //fn test2(string1 : CString, i : *mut c_int);
}

fn main() {
    trying();
}

fn trying() {
    unsafe {
        let mut string1 = CString::new("Likhe jo khat tujhe, jo teri yaad mee...........").expect("lol error in payload");
        let mut i : c_int = 69420;

        test1(&mut i);
        test2(&mut string1, &mut i);
        //test2(string1, &mut i);
    }
}

In the above snippet, I called C functions (declared earlier) in trying function. Also, I declare a string and int, and passed its pointer to test1 and test2 functions. The cargo run output is:

in test 1 :::
i pointer value is : 0x7fff2d8fbbc4
i -> value in that address is : 69420
in test 2 :::
i pointer value is : 0x7fff2d8fbbc4
i value in that address is : 69420
string1 value is : ���FV

This is expected output. I got the values and addresses.

But: the bug is…

When I change the way I was passing CStringin main.rs :

extern "C" {
    fn test1(i : *mut c_int);
    //fn test2(string1 : *mut CString, i : *mut c_int);
    fn test2(string1 : CString, i : *mut c_int);
}

in trying() function:

        test1(&mut i);
        //test2(&mut string1, &mut i);
        test2(string1, &mut i);

Now the cargo run output is:

in test 1 :::
i pointer value is : 0x7fffdfbeff10
i -> value in that address is : 69420
in test 2 :::
i pointer value is : 0x31
Segmentation fault

test1 is running perfectly and giving expected output, but in test2 output, somehow, making changes in CString affects the output of i integer pointer this shouldn't happen as changes in the first parameter shouldn't affect the second parameter.

What I think might be happening 🤔

This is the part where I procrastinated, pulled my hair, cried, lost interest, again got interested, and experienced lots of drama 😭 . I will try to keep things short and simple.

In order to find the reason why this was happening, I really learned a lot, and it made me more interested in low-level stuff.

So, when you are calling a function in C at the assembly level, it uses the calling convention called cdecl (C declaration), which works like this.

  1. Return address of callee (the function being called) is pushed onto the stack.
  2. The parameters are then pushed from right to left.

In our case, first return address, then integer pointer, then character pointer is pushed. We know that stack grows downwards, i.e. from higher to lower addresses.

So when we try to visualize this on memory, this might look like this:

So when I try to put a whole string in the char* it overflows to int*.

And that’s why integer pointer is getting affected.

Thank you so much for taking your time to read this blog. If you have any suggestion, correction or anything, I will be more than happy to hear from you and learn from you.

Feel free to contact me on these dating websites : LinkedIn : https://www.linkedin.com/in/saksham-jain-0b9144211/ X : https://twitter.com/RxW_777 Telegram : https://t.me/RxW777


메타데이터
post_id
b9cbdaaaffef
slug
how-i-got-my-first-cve-in-rust-b9cbdaaaffef
url
https://medium.com/@RxW777/how-i-got-my-first-cve-in-rust-b9cbdaaaffef
canonical_url
https://medium.com/@RxW777/how-i-got-my-first-cve-in-rust-b9cbdaaaffef
author_url
https://medium.com/@RxW777
status
ok
fetched_at
2026-07-24 03:33:04