Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add golang elf parse and probe config file #677

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.GsonBuilder;
import com.security.smith.client.message.*;
import com.security.smith.common.ProcessHelper;
import com.security.smith.log.SmithLogger;
Expand Down Expand Up @@ -282,7 +283,8 @@ private void readMessage() {
JsonArray jsonArray = jsonElement.getAsJsonArray();
for (JsonElement element : jsonArray) {
if (element.isJsonObject()) {
Message message = new Gson().fromJson(element, Message.class);
Gson gson = new GsonBuilder().registerTypeAdapter(Message.class, new MessageDeserializer()).create();
Message message = gson.fromJson(element, Message.class);
onMessage(message);
}
}
Expand Down
3 changes: 2 additions & 1 deletion rasp/librasp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ libc = "0.2.80"
nix = "0.24"
anyhow = "1.0.38"
# elf
goblin = "0.3.4"
goblin = "0.8.0"
byteorder = "1.0"
# lru cache
lru_time_cache = "0.11.8"
memmap = "0.7.0"
Expand Down
92 changes: 85 additions & 7 deletions rasp/librasp/src/golang.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use log::*;
use std::fs::File;
use std::{fs, path::PathBuf, process::Command};

use std::io::Read;
use regex::Regex;
use anyhow::{anyhow, Result};
use goblin::elf::Elf;
use memmap::MmapOptions;
Expand All @@ -10,7 +11,9 @@ use crate::async_command::run_async_process;
use crate::process::ProcessInfo;
use crate::runtime::{ProbeCopy, ProbeState, ProbeStateInspect};
use crate::settings;
use crate::parse_elf::{find_by_section, find_by_symbol};

const GOLANG_SUPPORT_VERSION: u32 = 22;
pub struct GolangProbeState {}

impl ProbeStateInspect for GolangProbeState {
Expand Down Expand Up @@ -105,10 +108,10 @@ pub fn golang_attach(pid: i32) -> Result<bool> {
Ok(true)
} else if es_code == 255 {
let msg = format!(
"golang attach exit code 255: {} {} {} {}",
es_code, pid, &stdout, &stderr
"golang attach exit code 255: {} {} {}",
es_code, &stdout, &stderr
);
error!("{}", msg);
error!("pid: {}, {}", pid, msg);
Err(anyhow!("{}", msg))
} else {
let msg = format!(
Expand All @@ -123,7 +126,7 @@ pub fn golang_attach(pid: i32) -> Result<bool> {
};
}

pub fn golang_bin_inspect(bin_file: PathBuf) -> Result<u64> {
pub fn golang_bin_inspect(bin_file: &PathBuf) -> Result<u64> {
let metadata = match fs::metadata(bin_file.clone()) {
Ok(md) => md,
Err(e) => {
Expand All @@ -141,11 +144,86 @@ pub fn golang_bin_inspect(bin_file: PathBuf) -> Result<u64> {
let shstrtab = elf.shdr_strtab;
for section in elf.section_headers.iter() {
let offset = section.sh_name;
if let Some(name) = shstrtab.get(offset) {
if name.unwrap() == ".gopclntab" {
if let Some(name) = shstrtab.get_at(offset) {
if name == ".gopclntab" {
return Ok(size);
}
}
}
return Ok(0);
}

pub fn parse_version(version: &String) -> Result<String> {
let re = Regex::new(r"^go(\d+\.\d+)(?:\.\d+)?").unwrap();

// use regex to extract the version number from the string
if let Some(captures) = re.captures(version) {
if let Some(version_number) = captures.get(1) {
let extracted_version = version_number.as_str();
return Ok(extracted_version.to_string());
}
}
return Err(anyhow::anyhow!("Failed to extract version number, from: {}", version));
}

pub fn golang_version(bin_file: &PathBuf) -> Result<String> {
let mut file = File::open(bin_file)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;

// parse elf
let elf = match Elf::parse(&buffer) {
Ok(elf) => elf,
Err(err) => {
let msg = format!(
"Failed to parse ELF file: {}", err
);
warn!("{}", msg);
return Err(anyhow!("{}", msg));
}
};

if let Ok(version) = find_by_section(&elf, &buffer, &file) {
return parse_version(&version);
} else {
if let Ok(version) = find_by_symbol(&elf, &file) {
return parse_version(&version);
} else {
return Err(anyhow!("get go version by section and symbol failed"));
}
}

}

pub fn check_golang_version(ver: &String) -> Result<()> {
let major_minor: Option<(u32, u32)> = match ver.split('.').next() {
Some(major_str) => {
if let Ok(major) = major_str.parse::<u32>() {
if let Some(minor_str) = ver.split('.').nth(1) {
if let Ok(minor) = minor_str.parse::<u32>() {
Some((major, minor))
} else {
None
}
} else {
Some((major, 0))
}
} else {
None
}
}
None => None,
};

if let Some((major, minor)) = major_minor {
if major == 1 && minor <= GOLANG_SUPPORT_VERSION {
return Ok(());
} else {
let msg = format!("Golang version too big: {}", ver);
return Err(anyhow!(msg));
}
} else {
let msg = format!("golang version cannot parse: {}", ver);
return Err(anyhow!(msg));
}
}
1 change: 1 addition & 0 deletions rasp/librasp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod process;
pub mod runtime;
#[allow(non_snake_case)]
pub mod settings;
pub mod parse_elf;

pub mod async_command {
use std::io::{BufRead, BufReader};
Expand Down
88 changes: 35 additions & 53 deletions rasp/librasp/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
use std::path::Path;

use std::os::unix::fs::PermissionsExt;
use anyhow::{anyhow, Result, Result as AnyhowResult};
use crossbeam::channel::Sender;
use fs_extra::dir::{copy, create_all, CopyOptions};
use fs_extra::file::{copy as file_copy, CopyOptions as FileCopyOptions};
use fs_extra::file::{copy as file_copy, remove, CopyOptions as FileCopyOptions};
use libraspserver::proto::{PidMissingProbeConfig, ProbeConfigData};
use log::*;

use crate::cpython::{python_attach, CPythonProbe, CPythonProbeState};
use crate::golang::{golang_attach, GolangProbe, GolangProbeState};
use crate::golang::{check_golang_version, golang_attach, GolangProbe, GolangProbeState};
use crate::jvm::{check_java_version, java_attach, java_detach, JVMProbe, JVMProbeState};
use crate::nodejs::{check_nodejs_version, nodejs_attach, NodeJSProbe};
use crate::php::{php_attach, PHPProbeState};
Expand Down Expand Up @@ -41,8 +41,8 @@ impl RASPManager {
) -> AnyhowResult<()> {
debug!("starting comm with probe, target pid: {}", process_info.pid);
let mnt_namespace = process_info.get_mnt_ns()?;
let nspid = if let Some(nspid) = ProcessInfo::read_nspid(process_info.pid)? {
nspid
let nspid = if process_info.nspid != 0 {
process_info.nspid
} else {
process_info.pid
};
Expand Down Expand Up @@ -259,8 +259,8 @@ impl RASPManager {
}
}

serde_json::to_string(&valid_messages)?;
//self.write_message_to_config_file(pid, nspid, valid_messages_string)?;
let valid_messages_string = serde_json::to_string(&valid_messages)?;
self.write_message_to_config_file(pid, nspid, valid_messages_string)?;

Ok(())
}
Expand Down Expand Up @@ -324,9 +324,9 @@ impl RASPManager {
let runtime_info = &process_info.runtime.clone().unwrap();
let root_dir = format!("/proc/{}/root", process_info.pid);
let pid = process_info.pid;
ProcessInfo::read_nspid(pid)?.ok_or(anyhow!("can not read nspid: {}", pid))?;
let nspid = process_info.nspid;
// delete config
// self.delete_config_file(pid, nspid)?;
self.delete_config_file(pid, nspid)?;
let attach_result = match runtime_info.name {
"JVM" => match JVMProbeState::inspect_process(process_info)? {
ProbeState::Attached => {
Expand Down Expand Up @@ -371,7 +371,7 @@ impl RASPManager {
let to = format!("{}{}",root_dir.clone(), settings::RASP_JAVA_AGENT_BIN());
let _ = self.copy_file_from_to_dest(settings::RASP_JAVA_JATTACH_BIN(), root_dir.clone());
let _ = self.copy_file_from_to_dest(settings::RASP_JAVA_AGENT_BIN(), root_dir.clone());
info!("copy from jattach/SmithAgent.jar to {}", to.clone());
info!("copy from java/SmithAgent.jar to {}", to.clone());
}
}
Err(e) => {
Expand Down Expand Up @@ -430,6 +430,14 @@ impl RASPManager {
Ok(true)
}
ProbeState::NotAttach => {
if !runtime_info.version.is_empty() {
match check_golang_version(&runtime_info.version) {
Ok(_) => {}
Err(e) => {
return Err(anyhow!(e));
}
}
}
let mut golang_attach = |pid: i32, bpf: bool| -> AnyhowResult<bool> {
if bpf {
if let Some(bpf_manager) = self.ebpf_comm.as_mut() {
Expand Down Expand Up @@ -881,66 +889,40 @@ impl MntNamespaceTracer {
}

impl RASPManager {
/*
pub fn write_message_to_config_file(
&self,
pid: i32,
nspid: i32,
message: String,
) -> AnyhowResult<()> {
let config_dir = "/var/run/elkeid_rasp";
let config_dir = format!("/proc/{}/root/var/run/elkeid_rasp", pid);
let config_path = format!("{}/{}.json", config_dir, nspid);
let config_path_bak = format!("{}.bak", config_path);
debug!("write message to {} {}", config_path_bak, message);
crate::async_command::run_async_process(
Command::new(crate::settings::RASP_NS_ENTER_BIN()).args([
"-m",
"-t",
pid.to_string().as_str(),
"sh",
"-c",
"PATH=/bin:/usr/bin:/sbin",
format!(
"mkdir -p {} && echo '{}' > {} && mv {} {}",
config_dir, message, config_path_bak, config_path_bak, config_path
)
.as_str(),
]),
)?;
let ns_thread = thread::Builder::new().spawn(move || -> AnyhowResult<()> {
debug!("switch namespace");
libraspserver::ns::switch_namespace(pid);
if !Path::new(&config_dir).exists() {
fs_extra::dir::create(config_dir, true)?;
}
fs_extra::file::write_all(&config_path_bak, message.as_str())?;
let mut option = fs_extra::file::CopyOptions::new();
option.overwrite = true;
fs_extra::file::move_file(config_path_bak, config_path, &option)?;
Ok(())
}).unwrap();
ns_thread.join()?;
info!("write message to {} {}", config_path_bak, message);

if !Path::new(&config_dir).exists() {
fs_extra::dir::create(&config_dir, true)?;
}
fs::set_permissions(&config_dir, fs::Permissions::from_mode(0o666))?;
fs_extra::file::write_all(&config_path_bak, message.as_str())?;
fs::set_permissions(&config_path_bak, fs::Permissions::from_mode(0o777))?;
let mut option = fs_extra::file::CopyOptions::new();
option.overwrite = true;
fs_extra::file::move_file(config_path_bak, config_path, &option)?;
info!("write message success");

Ok(())
}

pub fn delete_config_file(&self, pid: i32, nspid: i32) -> AnyhowResult<()> {
let config_path = format!("/var/run/elkeid_rasp/{}.json", nspid);

let config_path = format!("/proc/{}/root/var/run/elkeid_rasp/{}.json", pid, nspid);
if Path::new(&config_path).exists() {
crate::async_command::run_async_process(
Command::new(crate::settings::RASP_NS_ENTER_BIN()).args([
"-m",
"-t",
pid.to_string().as_str(),
"sh",
"-c",
format!("rm {}", config_path).as_str(),
]),
)?;
info!("delete config file: {}", config_path);
remove(config_path)?
}
Ok(())
}
*/
}

fn read_dir<P>(path: P) -> AnyhowResult<Vec<fs::DirEntry>>
Expand Down
Loading
Loading