Taint analysis
&
Serde's serialize_struct "quirk"
@disconnect3d_pl
Taint analysis 101
"Taint analysis is a process (...) to identify the flow of user input through �a system to understand the security implications of the system design"
Taint analysis 101
Why you may want taint tracking?
Rust taint analysis projects
Example of taint analysis in Rust�(from LiHRaM/taint)
// Program takes unsanitised input in one branch of an if statement. Since we can't at compile �// time say which branch will be taken, we must assume that b may be tainted and throw an error.
#![feature(register_tool)]
#![register_tool(taint)]
fn main() {
//This input is not an issue, as we allow input to be used to decide control flow
let a = input();
let b;
if a < 5 {
b = input(); // This input is an issue, as b may be used in the output function.
} else {
b = 5;
}
output(b); //~ ERROR function `output` received tainted input [T0001]
}
#[taint::source]
fn input() -> i32 {
4
}
#[taint::sink]
fn output(_: i32) {
()
}
example from https://github.com/LiHRaM/taint
// Program takes unsanitised input in one branch of an if statement. Since we can't at compile �// time say which branch will be taken, we must assume that b may be tainted and throw an error.
#![feature(register_tool)]
#![register_tool(taint)]
fn main() {
//This input is not an issue, as we allow input to be used to decide control flow
let a = input();
let b;
if a < 5 {
b = input(); // This input is an issue, as b may be used in the output function.
} else {
b = 5;
}
output(b); //~ ERROR function `output` received tainted input [T0001]
}
#[taint::source]
fn input() -> i32 {
4
}
#[taint::sink]
fn output(_: i32) {
()
}
example from https://github.com/LiHRaM/taint
// Program takes unsanitised input in one branch of an if statement. Since we can't at compile �// time say which branch will be taken, we must assume that b may be tainted and throw an error.
#![feature(register_tool)]
#![register_tool(taint)]
fn main() {
//This input is not an issue, as we allow input to be used to decide control flow
let a = input();
let b;
if a < 5 {
b = input(); // This input is an issue, as b may be used in the output function.
} else {
b = 5;
}
output(b); //~ ERROR function `output` received tainted input [T0001]
}
#[taint::source]
fn input() -> i32 {
4
}
#[taint::sink]
fn output(_: i32) {
()
}
example from https://github.com/LiHRaM/taint
// Program takes unsanitised input in one branch of an if statement. Since we can't at compile �// time say which branch will be taken, we must assume that b may be tainted and throw an error.
#![feature(register_tool)]
#![register_tool(taint)]
fn main() {
//This input is not an issue, as we allow input to be used to decide control flow
let a = input();
let b;
if a < 5 {
b = input(); // This input is an issue, as b may be used in the output function.
} else {
b = 5;
}
output(b); //~ ERROR function `output` received tainted input [T0001]
}
#[taint::source]
fn input() -> i32 {
4
}
#[taint::sink]
fn output(_: i32) {
()
}
example from https://github.com/LiHRaM/taint
// Program takes unsanitised input in one branch of an if statement. Since we can't at compile �// time say which branch will be taken, we must assume that b may be tainted and throw an error.
#![feature(register_tool)]
#![register_tool(taint)]
fn main() {
//This input is not an issue, as we allow input to be used to decide control flow
let a = input();
let b;
if a < 5 {
b = input(); // This input is an issue, as b may be used in the output function.
} else {
b = 5;
}
output(b); //~ ERROR function `output` received tainted input [T0001]
}
#[taint::source]
fn input() -> i32 {
4
}
#[taint::sink]
fn output(_: i32) {
()
}
example from https://github.com/LiHRaM/taint
// Program takes unsanitised input in one branch of an if statement. Since we can't at compile �// time say which branch will be taken, we must assume that b may be tainted and throw an error.
#![feature(register_tool)]
#![register_tool(taint)]
fn main() {
//This input is not an issue, as we allow input to be used to decide control flow
let a = input();
let b;
if a < 5 {
b = input(); // This input is an issue, as b may be used in the output function.
} else {
b = 5;
}
output(b); //~ ERROR function `output` received tainted input [T0001]
}
#[taint::source]
fn input() -> i32 {
4
}
#[taint::sink]
fn output(_: i32) {
()
}
example from https://github.com/LiHRaM/taint
serde serialize_struct�quirk
???
🤷 unused _len
How to live?
Use Semgrep or Dylint (TBD)
rules:
- id: incorrect-serialize-struct
message: "Serializing a structure with the incorrect number of fields."
languages: [rust]
severity: ERROR
patterns:
- pattern-either:
- pattern: |
let $X = $S.serialize_struct($NAME, $T)?;
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 1)?;
...
$X.serialize_field(...);
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 2)?;
...
$X.serialize_field(...);
...
$X.serialize_field(...);
...
$X.end()
rules:
- id: incorrect-serialize-struct
message: "Serializing a structure with the incorrect number of fields."
languages: [rust]
severity: ERROR
patterns:
- pattern-either:
- pattern: |
let $X = $S.serialize_struct($NAME, $T)?;
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 1)?;
...
$X.serialize_field(...);
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 2)?;
...
$X.serialize_field(...);
...
$X.serialize_field(...);
...
$X.end()
rules:
- id: incorrect-serialize-struct
message: "Serializing a structure with the incorrect number of fields."
languages: [rust]
severity: ERROR
patterns:
- pattern-either:
- pattern: |
let $X = $S.serialize_struct($NAME, $T)?;
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 1)?;
...
$X.serialize_field(...);
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 2)?;
...
$X.serialize_field(...);
...
$X.serialize_field(...);
...
$X.end()
rules:
- id: incorrect-serialize-struct
message: "Serializing a structure with the incorrect number of fields."
languages: [rust]
severity: ERROR
patterns:
- pattern-either:
- pattern: |
let $X = $S.serialize_struct($NAME, $T)?;
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 1)?;
...
$X.serialize_field(...);
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 2)?;
...
$X.serialize_field(...);
...
$X.serialize_field(...);
...
$X.end()
rules:
- id: incorrect-serialize-struct
message: "Serializing a structure with the incorrect number of fields."
languages: [rust]
severity: ERROR
patterns:
- pattern-either:
- pattern: |
let $X = $S.serialize_struct($NAME, $T)?;
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 1)?;
...
$X.serialize_field(...);
...
$X.end()
- pattern-not: |
let $X = $S.serialize_struct($NAME, 2)?;
...
$X.serialize_field(...);
...
$X.serialize_field(...);
...
$X.end()
Who use Clippy?
Who heard about Dylint?
Dylint